網站搜尋

reactjs - 嘗試在 react redux 中封裝 dispatch 函數


嗨,最近我遇到了 useDispatch 這個 hook,它應該可以作為 mapDispatchToProps 的替代方案,但我發現每次在 onPress 中都要寫 () => dispatch(action(args)) 很重複,所以我開始考慮一些通用的方法。我的目標是製作一個 hook,它使用 useDispatch() 並包裝它收到的函數,回傳 () => dispatch(theWrappedAction(theActionArgs))。例如,如果我有一個 action upCounterActionCreator,它如下所示:

export const upCounterActionCreator = (count: number = 1): AppActions => {
const action: UpCounterAction = {
    type: 'UP_COUNTER',
    count
};
return action;
};

我的目標是做類似這樣的事情:
const [upAfterDispatch]=useActions(upCounterActionCreator);

然後我可以做:

<Button onPress={upAfterDispatch(1)} title='+' />

我嘗試做的如下:

export const useActions = (...actions: ((...args: any) => AppActions)[]) => {
const dispatch = useDispatch<Dispatch<AppActions>>();
const actionsWithDispach: ((...args: any) => () => (...args: any) => AppActions)[] = [];

actions.forEach(action => {
    actionsWithDispach.push((...args: any) => () => (...args: any) => dispatch(action(...args)));
});
return actionsWithDispach;

};

要把那個包裹的函數放在 onPress 上,我需要做

<Button onPress={upAfterDispatch(1)()} title='+'' /> - 調用它不是一個很好的選擇。 然後當我調用它時,動作確實被派發了,然而當我在 payload 中調試時,我得到的是一個對象,而不是計數,其內容如下:它是一個類-

我哪裡做錯了?我需要做什麼才能:

  1. 獲取數字1(在操作有效負載中傳送的count參數)而不是類別

  2. 調用從 useActions 返回的函數,而不是像這樣調用它 onPress={upAfterDispatch(1)()

** 我認為在 args 中接收到的對象是 React Native 的 onPress 事件,如何避免它覆蓋我的 count 參數?提前感謝!

答案#1

我認為這就是你想做的:

export const useActions = (...actions: ((...args: any) => AppActions)[]) => {
  const dispatch = useDispatch<Dispatch<AppActions>>();
  const actionsWithDispach: ((...args: any) => () => (...args: any) => AppActions)[] = [];

  actions.forEach(action => {
    actionsWithDispach.push((...args: any) => () => dispatch(action(...args)));
  });
  return actionsWithDispach;
};

你多加了一個 (...args: any) =>,但根據上面的程式碼,你可以寫成 onClick={theAction(1)}

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore } = Redux;
const initialState = {
  count: 0,
};
const reducer = (state, { type, payload }) => {
  if (type === 'UP') {
    return { count: state.count + payload };
  }
  return state;
};
const store = createStore(
  reducer,
  { ...initialState },
  window.__REDUX_DEVTOOLS_EXTENSION__ &&
    window.__REDUX_DEVTOOLS_EXTENSION__()
);
//action
const add = (howMuch) => ({ type: 'UP', payload: howMuch });
const useAction = (action) => {
  const dispatch = useDispatch();

  return React.useMemo(
    () => (...args) => () => dispatch(action(...args)),
    [action, dispatch]
  );
};
const Button = React.memo(function Button({ up, howMuch }) {
  const rendered = React.useRef(0);
  rendered.current++;
  return (
    <button onClick={up(howMuch)}>
      Rendered: {rendered.current} times, add {howMuch}
    </button>
  );
});

const App = () => {
  const up = useAction(add);
  const count = useSelector((state) => state.count);
  return (
    <div>
      <h2>count:{count}</h2>
      <Button up={up} howMuch={1} />
      <Button up={up} howMuch={1} />
      <Button up={up} howMuch={1} />
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>


<div id="root"></div>

答案#2

type ActionCreator = (...args: any) => AppActions
export const useActions = (...actions: ActionCreator[]) => {
  const dispatch = useDispatch<Dispatch<AppActions>>();
  return actions.map(action => (...args: any) => () => dispatch(action(...args)))
}