簡體   English   中英

useReducer 和 useEffect 鈎子來獲取數據

[英]useReducer and useEffect hooks to fetch data

我正在試驗useEffect鈎子,但我對如何使用useEffectuseReducer感到有些困惑。

我有一個帶有幾個復選框的簡單頁面,在更改每個復選框時,我應該重新查詢 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM