Reactコンテキストでリデューサーを動作させるのに問題があります。 buttonbar.js
、状態を更新することになっている2つのボタンがあります。状態は、現在のデータをフィルタリングすることによって更新されます。ボタンがクリックされ、エラーは発生しませんが、何も実行されていません。問題は減速機にあると思います。
context.js
import React, { useState, useEffect } from "react";
import * as moment from "moment";
import axios from "axios";
export const Context = React.createContext();
const url = "https://projects.fivethirtyeight.com/polls/polls.json";
export const filterReducer = (state, action) => {
switch (action.type) {
case "SHOW_ALL":
return state.polls;
case "SHOW_APPROVAL":
return state.polls.filter(e => e.type === "trump-approval");
default:
return state.polls;
}
};
export function Provider({ children }) {
let intialState = {
polls: [],
dispatch: action => this.setState(state => filterReducer(state, action))
};
const [state, setState, dispatch] = useState(intialState);
useEffect(() => {
var dateRange = moment()
.subtract(7, "days")
.calendar();
axios
.get(url)
.then(res => {
setState({
polls: res.data
.filter(e => Date.parse(e.endDate) >= Date.parse(dateRange))
.reverse()
});
}, [])
.catch(error => console.log(error));
}, []);
return (
<Context.Provider value={[state, setState, dispatch]}>
{children}
</Context.Provider>
);
}
// export const Consumer = Context.Consumer;
buttonbar.js
import React, { useContext, useState, useEffect, useReducer } from "react";
import { Context, filterReducer } from "../context";
const ButtonBar = () => {
const [state, setState] = useContext(Context);
const [filter, dispatch] = useReducer(filterReducer, state);
const showAll = () => {
dispatch({ type: "SHOW_ALL" });
console.log("showAll clicked");
};
const showApproval = () => {
dispatch({ type: "SHOW_APPROVAL" });
console.log("showApproval clicked");
};
return (
<div class="mb-2">
<button class="btn btn-primary btn-sm" name="all" onClick={showAll}>
All
</button>{" "}
<button
class="btn btn-primary btn-sm"
name="trump approval"
onClick={showApproval}
>
Trump Approval
</button>
</div>
);
};
export default ButtonBar;
いくつかのことがあり、あなたは正しく行っていません。
最初、ディスパッチメソッドでinitialStateを使用しており、代わりに3番目の引数を使用してuseState
からこのディスパッチ値を取得しようとしています。間違っている
2番目、レデューサーパターンを使用しているため、useReducer
フックの場合に使用することをお勧めします
3番目、レデューサーでデータをフィルターすることはできません。そうしないと、次回すべてのデータを表示するときに、完全なデータが失われ、フィルターされたデータのみが失われます。データは残ります。代わりに、セレクターが必要です。
関連コード:
import React, {
useEffect,
useContext,
useReducer,
useMemo,
useState
} from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import moment from "moment";
import axios from "axios";
export const Context = React.createContext();
const url = "https://projects.fivethirtyeight.com/polls/polls.json";
export const filterReducer = (state, action) => {
switch (action.type) {
case "ADD_POLLS":
console.log(action.payload);
return action.payload;
default:
return state.polls;
}
};
export function Provider({ children }) {
const [state, dispatch] = useReducer(filterReducer);
useEffect(() => {
var dateRange = moment()
.subtract(7, "days")
.calendar();
axios
.get(url)
.then(res => {
dispatch({
type: "ADD_POLLS",
payload: res.data
.filter(e => Date.parse(e.endDate) >= Date.parse(dateRange))
.reverse()
});
}, [])
.catch(error => console.log(error));
}, []);
return (
<Context.Provider value={[state, dispatch]}>{children}</Context.Provider>
);
}
const ButtonBar = () => {
const [polls] = useContext(Context);
const [state, setState] = useState(polls);
useEffect(() => {
setState(polls);
}, [polls]);
const filterResult = useMemo(() => {
return filter => {
switch (filter) {
case "SHOW_ALL":
setState(polls);
break;
case "SHOW_APPROVAL":
setState(polls.filter(e => e.type === "trump-approval"));
break;
default:
return;
}
};
}, [polls]);
return (
<div class="mb-2">
<button
class="btn btn-primary btn-sm"
name="all"
onClick={() => filterResult("SHOW_ALL")}
>
All
</button>{" "}
<button
class="btn btn-primary btn-sm"
name="trump approval"
onClick={() => filterResult("SHOW_APPROVAL")}
>
Trump Approval
</button>
<div>{(state || []).length}</div>
<pre>{JSON.stringify(state, null, 4)}</pre>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider>
<ButtonBar />
</Provider>,
rootElement
);
コンポーネントでuseReducerフックを使用しているからといって、useReducerフックを誤って使用しても、グローバルコンテキスト状態を更新しているわけではありません。
だからあなたのbuttonbar.jsで
const [filter, dispatch] = useReducer(filterReducer, state);
const showAll = () => {
dispatch({ type: "SHOW_ALL" });
console.log("showAll clicked");
};
const showApproval = () => {
dispatch({ type: "SHOW_APPROVAL" });
console.log("showApproval clicked");
};
レデューサーを使用して状態を正しく更新していますが、グローバルコンポーネントの状態ではなくローカルコンポーネントの状態のみを更新します。
あなたがreduxから来ている場合、これは直感に反するように見えます。
コンテキストでは、状態は親コンポーネントに含まれ、変更されるため、上記のコードを親コンポーネントに移動し、コンテキストを介してアクセスします。
export function Provider({ children }) {
let intialState = {
polls: [],
dispatch: action => this.setState(state => filterReducer(state, action))
};
// 2 args not 3
const [state, setState] = useState(intialState);
const [filter, dispatch] = useReducer(filterReducer, state);
const showAll = () => {
dispatch({ type: "SHOW_ALL" });
console.log("showAll clicked");
};
const showApproval = () => {
dispatch({ type: "SHOW_APPROVAL" });
console.log("showApproval clicked");
};
状態と関数を値propに渡す
<Context.Provider value={{
showAllProp: () => showAll(),
showApprovalProp: () => showApproval(),
filterProp: filter }}>
{children}
</Context.Provider>
次に、値のプロパティを使用して子コンポーネントのこれらの値と関数にアクセスできます。
const context = useContext(Context);
<button class="btn btn-primary btn-sm" name="all" onClick={context.showAllProp}>
All
</button>{" "}
<button
class="btn btn-primary btn-sm"
name="trump approval"
onClick={context.showApprovalProp}
>
これは基本的に、コンテキストをコンポーネントに接続する方法です。