如何在createSlice reducer中访问RTK-Query数据?以全选功能为例
在createSlice的reducer中访问RTK-Query数据的正确方式是什么?
以全选功能为例:能否在toggleSelectAll动作中直接获取当前useGetOrdersQuery()的数据?还是实现全选/取消全选的唯一/最佳方式是将useGetOrdersQuery()的数据作为动作payload传入toggleSelectAll?
相关代码示例
dashboardSlice.js
const initialState = { selectedIds: [], }; export const dashboardSlice = createSlice({ name: 'dashboard', initialState, reducers: { toggleSelectAll: (state, action) => { //get "useGetOrdersQuery" data id's and assign to state.selectedIds } } }); export const { toggleSelectAll } = dashboardSlice.actions; export const selectSelectedIds = state => state.dashboard.selectedIds;
orders-list.js
import { useGetOrdersQuery } from './api' import { toggleSelectAll, selectSelectedIds } from './dashboardSlice'; const OrdersList = () => { const {data} = useGetOrdersQuery(123); const dispatch = useDispatch(); const selectedIds = useSelector(selectSelectedIds); return ( <div> <button onClick={() => dispatch(toggleSelectAll())}> select/deselect all </button> {data.map(o => ( <div> <h2>{o.name}</h2> <input type="checkbox" checked={selectedIds.includes(o.id)} /> </div> ))} </div> ) }
api.js
export const api = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: '/api' }), endpoints: (builder) => ({ getOrders: builder.query({ query: (userId) => `${userId}/orders`, providesTags: [{ type: 'Orders', id: 'LIST' }] }) }) }); export const { useGetOrdersQuery } = api;
解答
绝对不能在reducer中直接访问RTK-Query数据
Reducer必须是纯函数,只能依赖传入的state和action参数,不能主动读取Redux store中的其他部分(包括RTK-Query缓存的数据)。RTK-Query的数据存在state[api.reducerPath]下,但reducer里硬编码获取这部分数据会破坏纯函数特性,导致逻辑耦合、可维护性下降。最佳实现:将订单ID列表作为payload传入动作
组件中已经通过useGetOrdersQuery拿到了订单数据,直接把订单ID列表作为参数传给toggleSelectAll动作即可。同时在reducer里根据当前选中状态判断是执行全选还是取消全选:
修改后的组件代码(orders-list.js)
// 按钮点击时传入所有订单ID <button onClick={() => dispatch(toggleSelectAll(data.map(o => o.id)))}> select/deselect all </button>
修改后的Slice代码(dashboardSlice.js)
reducers: { toggleSelectAll: (state, action) => { const allOrderIds = action.payload; // 判断当前是否已经全选所有订单 const isAllSelected = allOrderIds.every(id => state.selectedIds.includes(id)); if (isAllSelected) { // 取消全选:清空选中ID列表 state.selectedIds = []; } else { // 执行全选:设置为所有订单ID state.selectedIds = [...allOrderIds]; } } }
- 可选方案(不推荐)
你也可以通过createSelector创建一个整合了选中状态和RTK-Query数据的选择器,在组件中先判断全选状态,再dispatch对应的全选/取消全选动作。但这种方式会增加组件逻辑复杂度,不如直接传payload简洁直观。
总结:将订单数据作为payload传入动作是最符合Redux规范、逻辑清晰的实现方式。
内容的提问来源于stack exchange,提问作者shkipper
相关产品推荐
相关产品推荐

