[英]useReducer and useEffect hooks to fetch data
我正在試驗useEffect
鈎子,但我對如何使用useEffect
和useReducer
感到有些困惑。
我有一個帶有幾個復選框的簡單頁面,在更改每個復選框時,我應該重新查詢 api 以加載新數據。
我的想法是對組件進行如下編碼:
export const Page: React.FunctionComponent = () => {
// state to manage the list returned by the api
const [list, setList] = useState<any[]>([]);
// this should manage the state related to the checkboxes
const [checkboxesState, dispatch] = useReducer(reducer, initialCheckboxesState);
useEffect(() => {
// load data from api
loadProducts(checkboxesState);
}, [checkboxesState]); // my idea here is that when checkboxesState changes, it should trigger the 'useEffect' hook
}
現在關於 useReducer
// initial state
const initialFiltersState: CheckboxesState = {
country: [], // array of strings for countries ie ['USA', 'Brazil', 'India']
};
function reducer(
checkboxesState: CheckboxesState,
action: { type: string; value: string; checked: boolean }
) {
const index = checkboxesState[action.type].indexOf(action.value);
// if checkbox is ticked and it's not already present in the array, insert it
if (action.checked && index === -1) {
checkboxesState[action.type].push(action.value);
} else if (!action.checked && index > -1) {
// if it's present and we are unchecking it, remove it from the array
checkboxesState[action.type].splice(index, 1);
}
// TODO: this is not the best way to handle reload. I don't want to do it here, this should just trigger the useEffect
loadProducts(productFilters);
return checkboxesState;
}
// this is the handler for checkbox changes
const onChangeHandler = (event: any): void => {
dispatch({
type: event.target.name, // eg. country
value: event.target.value, // eg. Australia
checked: event.target.checked,// bool
});
};
現在我知道我在做的事情可能很愚蠢,但我已經堅持了很長一段時間。 知道為什么在checkboxesState
更改時不調用 useEffect 嗎? 還是我完全偏離了軌道?
我試圖將JSON.stringify(checkboxesState)
傳遞給 useEffect,但也沒有運氣。
我的組件中有更多不相關的內容,因此我嘗試僅將與特定任務相關的代碼放在這里。 我希望我清楚地解釋了自己
您沒有正確更新狀態。
狀態更新時未觸發useEffect
原因是因為您正在直接改變狀態。 您正在從減速器返回相同的狀態對象。 因此,就 React 而言,reducer 函數在調用時不會更新狀態。 因此,不會再次觸發useEffect
。
您需要從 reducer 返回一個新的 state 對象,以便 React 知道該 state 已被 reducer 更新。 修改您的減速器以在每次更新狀態時返回一個新對象。
另一種解決方案可能是使用 thunk 動作,這些動作總是可以訪問最近的狀態並且可以分派多個動作。 Thunk 動作甚至可以調用其他 thunk 動作並等待它們(如果 thunk 動作返回承諾): thunkAction(arg)(dispatch,getState).then(thunkActionResult=>otherStuff)
。
Thunk 有很好的文檔記錄並且很容易被其他開發人員識別。
Thunk 是 Redux 的中間件,但可以通過自定義鈎子應用於 useReducer:
const { useRef, useState } = React; //custom hook, you can define in different file const compose = (...fns) => fns.reduce((result, fn) => (...args) => fn(result(...args)) ); const mw = () => (next) => (action) => next(action); const createMiddleware = (...middlewareFunctions) => ( store ) => compose( ...middlewareFunctions .concat(mw) .reverse() .map((fn) => fn(store)) ); const useMiddlewareReducer = ( reducer, initialState, middleware = () => (b) => (c) => b(c) ) => { const stateContainer = useRef(initialState); const [state, setState] = useState(initialState); const dispatch = (action) => { const next = (action) => { stateContainer.current = reducer( stateContainer.current, action ); return setState(stateContainer.current); }; const store = { dispatch, getState: () => stateContainer.current, }; return middleware(store)(next)(action); }; return [state, dispatch]; }; //middleware const thunkMiddleWare = ({ getState, dispatch }) => ( next ) => (action) => typeof action === 'function' ? action(dispatch, getState) : next(action); const logMiddleware = ({ getState }) => (next) => ( action ) => { console.log('in log middleware', action, getState()); Promise.resolve().then(() => console.log('after action:', action.type, getState()) ); return next(action); }; //your actions const init = { value: 'A' }; const TOGGLE = 'TOGGLE'; const later = () => new Promise((r) => setTimeout(() => r(), 500)); const otherThunk = () => (dispatch, getState) => { dispatch({ type: 'not relevant action form otherTunk' }); console.log('in other thunk, state is:', getState()); //note that I am returning a promise return later(); }; const thunkToggle = () => (dispatch, getState) => { //dispatching other thunk and waiting for it to finish otherThunk()(dispatch, getState).then(() => dispatch({ type: TOGGLE }) ); }; //your component code const reducer = (state, { type }) => { console.log(`in reducer action type: ${type}`); //toggle state.value between A and B if (type === TOGGLE) { return { value: state.value === 'A' ? 'B' : 'A' }; } return state; }; const middleware = createMiddleware( thunkMiddleWare, logMiddleware ); const App = () => { const [state, dispatch] = useMiddlewareReducer( reducer, init, middleware ); return ( <div> <button onClick={() => dispatch(thunkToggle())}> toggle </button> <pre>{JSON.stringify(state, undefined, 2)}</pre> </div> ); }; ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>
使用 thunk,您可以將邏輯移出組件並執行dispatch(fetchData(args))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.