如何使用 React Redux Toolkit 从商店获取数据并获取缓存版本(如果我已经请求)?
我需要请求多个用户,例如 user1、user2 和 user3。如果我在 user1 已经被请求之后提出请求,那么我不想再次从 API 中获取 user1。相反,它应该给我来自商店的 user1 的信息。
如何使用 Redux Toolkit 切片在 React 中执行此操作?
如何使用 React Redux Toolkit 从商店获取数据并获取缓存版本(如果我已经请求)?
我需要请求多个用户,例如 user1、user2 和 user3。如果我在 user1 已经被请求之后提出请求,那么我不想再次从 API 中获取 user1。相反,它应该给我来自商店的 user1 的信息。
如何使用 Redux Toolkit 切片在 React 中执行此操作?
Redux Toolkit 可以帮助解决这个问题,但我们需要在工具包中组合各种“工具”。
我们将创建一个useUser
自定义的 React 钩子来通过 id 加载用户。
我们需要在我们的钩子/组件中使用单独的钩子来读取数据 ( useSelector
) 和启动一个 fetch ( useDispatch
)。存储用户状态永远是 Redux 的工作。除此之外,在我们是否在 React 或 Redux 中处理某些逻辑方面还有一些余地。
我们可以看一下所选择的值user
在自定义挂钩,只有dispatch
在requestUser
当动作user
是undefined
。或者,我们可以dispatch
requestUser
所有的时间和有requestUser
thunk的检查,看是否需要做使用抓取condition
的设置createAsyncThunk
。
我们天真的方法只是检查用户是否已经存在于状态中。我们不知道是否有针对此用户的任何其他请求已在等待处理。
让我们假设你有一些函数接受一个 id 并获取用户:
const fetchUser = async (userId) => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
return res.data;
};
我们创建一个userAdapter
助手:
const userAdapter = createEntityAdapter();
// needs to know the location of this slice in the state
export const userSelectors = userAdapter.getSelectors((state) => state.users);
export const { selectById: selectUserById } = userSelectors;
我们创建了一个requestUser
thunk 动作创建器,它只在用户尚未加载时才执行提取:
export const requestUser = createAsyncThunk("user/fetchById",
// call some API function
async (userId) => {
return await fetchUser(userId);
}, {
// return false to cancel
condition: (userId, { getState }) => {
const existing = selectUserById(getState(), userId);
return !existing;
}
}
);
我们可以使用createSlice
来创建reducer。在userAdapter
帮助我们更新状态。
const userSlice = createSlice({
name: "users",
initialState: userAdapter.getInitialState(),
reducers: {
// we don't need this, but you could add other actions here
},
extraReducers: (builder) => {
builder.addCase(requestUser.fulfilled, (state, action) => {
userAdapter.upsertOne(state, action.payload);
});
}
});
export const userReducer = userSlice.reducer;
但是由于我们的reducers
财产是空的,我们也可以使用createReducer
:
export const userReducer = createReducer(
userAdapter.getInitialState(),
(builder) => {
builder.addCase(requestUser.fulfilled, (state, action) => {
userAdapter.upsertOne(state, action.payload);
});
}
)
我们的 React 钩子从选择器返回值,但也会触发dispatch
a useEffect
:
export const useUser = (userId: EntityId): User | undefined => {
// initiate the fetch inside a useEffect
const dispatch = useDispatch();
useEffect(
() => {
dispatch(requestUser(userId));
},
// runs once per hook or if userId changes
[dispatch, userId]
);
// get the value from the selector
return useSelector((state) => selectUserById(state, userId));
};
如果用户已经加载,前一种方法会忽略提取,但是如果它已经加载呢?我们可以对同一用户同时进行多次提取。
为了解决这个问题,我们的状态需要存储每个用户的获取状态。在文档示例中,我们可以看到它们在用户实体旁边存储了一个键控状态对象(您也可以将状态存储为实体的一部分)。
我们需要添加一个空status
字典作为我们的属性initialState
:
const initialState = {
...userAdapter.getInitialState(),
status: {}
};
我们需要更新状态以响应所有三个requestUser
操作。我们可以userId
通过查看 的meta.arg
属性来获得调用 thunk的action
:
export const userReducer = createReducer(
initialState,
(builder) => {
builder.addCase(requestUser.pending, (state, action) => {
state.status[action.meta.arg] = 'pending';
});
builder.addCase(requestUser.fulfilled, (state, action) => {
state.status[action.meta.arg] = 'fulfilled';
userAdapter.upsertOne(state, action.payload);
});
builder.addCase(requestUser.rejected, (state, action) => {
state.status[action.meta.arg] = 'rejected';
});
}
);
我们可以通过 id 从状态中选择一个状态:
export const selectUserStatusById = (state, userId) => state.users.status[userId];
我们的 thunk 在确定是否应该从 API 获取时应该查看状态。如果它已经'pending'
或,我们不想加载'fulfilled'
。如果是'rejected'
或,我们将加载undefined
:
export const requestUser = createAsyncThunk("user/fetchById",
// call some API function
async (userId) => {
return await fetchUser(userId);
}, {
// return false to cancel
condition: (userId, { getState }) => {
const status = selectUserStatusById(getState(), userId);
return status !== "fulfilled" && status !== "pending";
}
}
);