如何在redux-toolkit的事件处理函数中调用带参数的useSelector
问题本质
你的代码违反了React Hook的核心调用规则:所有Hook必须在React函数组件的顶层作用域,或自定义Hook的顶层作用域中调用,禁止嵌套在事件回调、循环、条件判断等内部函数中。
可行解决方案
方案1:组件顶层预取数据映射表
适合单次渲染中可能用到的媒体id数量不多的场景,点击响应速度最快。
调整selector返回id到数据的映射,在组件顶层提前调用useSelector拿到完整映射,点击时直接按id查表即可:
// selector层:封装返回id到数据的映射表 const playlistMediaMapSelector = createSelector( state => state.playlist, playlist => playlist.reduce((map, item) => { map[item.id] = item return map }, {}) ) // 组件内使用 const YourMediaComponent = () => { // Hook必须放在组件顶层调用 const mediaMap = useSelector(playlistMediaMapSelector) const handleMediaItemClick = (media: any): void => { // 点击时直接从预取的映射表中获取数据 const data = mediaMap[media.id] // 后续业务逻辑 } // 其余组件逻辑... }
方案2:事件内直接调用store实例取数
适合数据量极大、不需要预取全量数据的场景,无需改动现有selector逻辑。
redux selector本身是纯函数,只要传入最新的state和参数就能得到正确结果,直接在事件中通过store.getState()拿到最新状态传入selector即可:
// 引入项目中的redux store实例 import store from '你的store文件路径' const handleMediaItemClick = (media: any): void => { // 拿到当前最新的redux状态 const currentState = store.getState() // 直接调用selector传参取数,不需要useSelector // 如果你原selector的签名是 (state, id) => data,写法改为: // const data = playlistWithMediaSelector(currentState, media.id) const data = playlistWithMediaSelector(media.id)(currentState) // 后续业务逻辑 }
方案3:封装自定义Hook复用逻辑
适合多个组件需要复用相同点击取数逻辑的场景,符合Hook调用规则。
自定义Hook必须以use开头,内部可以合法调用Hook:
// 封装自定义Hook const useMediaClickHandler = () => { // 顶层合法调用useSelector,返回按id取数的方法 const getMediaById = useSelector(state => (id) => playlistWithMediaSelector(id)(state)) // 用useCallback缓存回调避免不必要重渲染 return useCallback((media: any) => { const data = getMediaById(media.id) // 通用处理逻辑 }, [getMediaById]) } // 组件内使用 const YourMediaComponent = () => { const handleMediaItemClick = useMediaClickHandler() return <div onClick={() => handleMediaItemClick(media)}>媒体项</div> }
内容的提问来源于stack exchange,提问作者ptimer
相关产品推荐
相关产品推荐

