问题描述
我是redux和redux工具包的新手。我正在从redux工具箱官方文档中阅读本教程。 https://redux-toolkit.js.org/api/createAsyncThunk。这是它提供的示例
import { createAsyncThunk,createSlice,unwrapResult } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',async (userId,{ getState,requestId }) => {
const { currentRequestId,loading } = getState().users
if (loading !== 'pending' || requestId !== currentRequestId) {
return
}
const response = await userAPI.fetchById(userId)
return response.data
}
)
const useRSSlice = createSlice({
name: 'users',initialState: {
entities: [],loading: 'idle',currentRequestId: undefined,error: null
},reducers: {},extraReducers: {
[fetchUserById.pending]: (state,action) => {
if (state.loading === 'idle') {
state.loading = 'pending'
state.currentRequestId = action.Meta.requestId
}
},[fetchUserById.fulfilled]: (state,action) => {
const { requestId } = action.Meta
if (state.loading === 'pending' && state.currentRequestId === requestId) {
state.loading = 'idle'
state.entities.push(action.payload)
state.currentRequestId = undefined
}
},[fetchUserById.rejected]: (state,action) => {
const { requestId } = action.Meta
if (state.loading === 'pending' && state.currentRequestId === requestId) {
state.loading = 'idle'
state.error = action.error
state.currentRequestId = undefined
}
}
}
})
const UsersComponent = () => {
const { users,loading,error } = useSelector(state => state.users)
const dispatch = usedispatch()
const fetchOneUser = async userId => {
try {
const resultAction = await dispatch(fetchUserById(userId))
const user = unwrapResult(resultAction)
showToast('success',`Fetched ${user.name}`)
} catch (err) {
showToast('error',`Fetch Failed: ${err.message}`)
}
}
// render UI here
}
我的问题是,因为我们已经处理了生命周期动作创建者即诺言被拒绝的情况。
[fetchUserById.rejected]: (state,action) => {
const { requestId } = action.Meta
if (state.loading === 'pending' && state.currentRequestId === requestId) {
state.loading = 'idle'
state.error = action.error
state.currentRequestId = undefined
}
}
那么,为什么在UsersComponent
中,我们仍然需要尝试捕获由调度程序引发的错误。我们是否不能从error
状态得知请求是否失败?更清楚地说,我如何通过以下方式写UsersComponent
:
const UsersComponent = () => {
const { users,error } = useSelector(state => state.users)
const dispatch = usedispatch()
useEffect(() => {
dispatch(fetchUserById(userId))
},[dispatch])
if(error) {
showToast('error',`Fetch Failed: ${error.message}`)
} else if(loading) {
showToast('loading')
} else {
showToast('success',`Fetched ${user.name}`)
}
// render UI here
}
此外,我不知道何时会派遣fetchUserById.rejected
?根据文档,createAsyncThunk
将始终返回已解决的承诺。如果我们在createAsyncThunk
if (loading !== 'pending' || requestId !== currentRequestId) {
return
}
那么,分派生命周期操作将是fulfilled
对吗?派遣rejected
生命周期动作需要什么?仅当回调内部引发异常时?
我的最后一个问题是,例如,如果我想检查返回的数据是否正确,我想确定是否有一个名为data
的属性。我该如何实现?如果我将createAsyncThunk
中的回调重写为
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',loading } = getState().users
if (loading !== 'pending' || requestId !== currentRequestId) {
return
}
const response = await userAPI.fetchById(userId)
if(!response.data) throw new Error('No data')
return response.data
}
)
在rejected
上实际上没有data
属性的情况下,是否会分派response
生命周期操作?
解决方法
关于您问题的第一部分: 是的,您可以在后续渲染中执行此操作。 但是如果你只是写
if(error) {
showToast('error',`Fetch failed: ${error.message}`)
} else if(loading) {
showToast('loading')
} else {
showToast('success',`Fetched ${user.name}`)
}
会在随后对该组件的每个渲染中显示敬酒。在React中,每次渲染父对象时,该组件都会重新渲染,因此您可能会看到很多Toast。
您当然也可以使用useEfect
,然后,如果您更喜欢的话,可以这样做。那只是一种风格,有些人喜欢一种更好的方式,另一种喜欢。
例如,rejected
可能由于网络问题而引发错误时,将调度userAPI.fetchById
动作。
如果仅调用return
,它将实际上返回undefined
并导致以undefined
作为有效载荷的已完成操作。