试图在反应 redux 中包装调度功能
Posted
技术标签:
【中文标题】试图在反应 redux 中包装调度功能【英文标题】:Trying to wrap dispatch function in react redux 【发布时间】:2020-08-25 10:10:23 【问题描述】:嗨,最近我遇到了 useDispatch 钩子,它应该给我一个 mapDispatchToProps 的替代方案,我发现在每个 onPress 中重复 () => dispatch(action(args))
所以我开始考虑一些通用的东西。我的目标是制作一个使用 useDispatch()
的钩子,并包装它获取的函数并返回 () => dispatch(theWrappedAction(theActionArgs))
例如,如果我有一个动作 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='+' />
- 调用它,这不是很好的选择。
然后,当我确实调用它时,确实正在调度该动作,但是当我在我的有效负载上进行调试时,我有一个计数对象,如下所示:
这是一个类-
我做错了什么?我需要做什么才能:
获取数字 1(动作负载中发送的计数参数)而不是类
从 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>
【讨论】:
@MD10 如果它不起作用,那么你做错了什么,我添加了一个工作代码 sn-p 与我的答案中的确切代码。这样做会导致不必要的渲染,因为您的动作每次都会重新创建,即使它们不需要创建。您可以在 useActions 中使用 useMemo 来防止这种情况发生。 我没注意,你说得对!非常感谢你是最棒的!你能告诉我如何在这里使用 useMemo 吗?只是包装整个功能?它的目的是什么? @MD10 我更新了答案,但使用 useMemo 您一次只能执行一项操作,否则即使操作没有更改,它也会重新创建操作。在示例中,您可以看到按钮没有重新呈现,因为它们是纯组件(使用 React.memo)并且向上操作永远不会改变(使用 useMemo) @MD10useMemo
不适用于多个参数,因为 ...args
每次调用钩子时都会创建一个新数组。因此,即使数组中的元素相同,useMemo 也会将其视为一个新数组,并且不会记忆调度动作函数。不记忆不一定会对性能造成很大影响,这取决于不必要的渲染发生的频率以及重新渲染的元素数量。它还取决于您的目标平台,低端手机将比高端台式机遭受更多损失。
@MD10 如果您在 Parent 中执行 <Child onChange=()=>console.log('hello') />
,那么您始终将 onChange 的新引用传递给该组件,即使回调永远不会改变。但是当 Parent 永远不会重新渲染时,Child 也不会。在我的示例中,我有一个名为 App 的组件,它在每次计数更改时重新呈现,但 App 中的 Button 组件永远不会重新呈现,因为它们在单击时所做的事情保持不变。【参考方案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)))
【讨论】:
如果我在调用之前添加 () => 就可以工作,因此它没有达到我的意图:/ 想避免使用 () => 总是 上面的评论解决了我的问题,只是想弄清楚如何使用 useMemo 钩子以及为什么——如上所述——你可以提供一个答案,我将不胜感激,不过谢谢!跨度>以上是关于试图在反应 redux 中包装调度功能的主要内容,如果未能解决你的问题,请参考以下文章