【问题标题】:Questions about using createAysncThunk关于使用 createAsyncThunk 的问题
【发布时间】:2020-12-09 10:22:03
【问题描述】:

我是 redux 和 redux 工具包的新手。我正在阅读 redux toolkit 官方文档中的本教程。 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中,我们还需要尝试捕获dispatch抛出的错误。难道我们不能从error状态判断请求是否失败吗?为了更清楚,我这样写UsersComponent怎么样:


const UsersComponent = () => {
  const { users, loading, 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',
  async (userId, { getState, requestId }) => {
    const { currentRequestId, 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
  }
)

response 上实际上没有data 属性时,rejected 生命周期操作是否会被调度?

【问题讨论】:

  • 请针对每个帖子关注一个问题,对于您的第一个问题:您的示例将无法运行,因为您发送了 asyncThunk,它返回了一个承诺,您可以轻松检查您的代码。跨度>
  • @DennisVash 它确实有效。我知道它会返回一个承诺。但是通过调度 asyncThunk,thunk 将调度待处理的动作,如果承诺成功解决,则调度已完成的动作,承诺值为 action.payload。
  • 那么问题出在哪里?如果它工作那没关系
  • @DennisVash 帖子的问题中说明了问题。如果你不想回答为什么不能忽略这个问题

标签: javascript reactjs redux redux-toolkit


【解决方案1】:

关于你问题的第一部分: 是的,您可以在后续渲染中执行此操作。 但是如果你只是写

  if(error) {
    showToast('error', `Fetch failed: ${error.message}`)
  } else if(loading) {
    showToast('loading')
  } else {
    showToast('success', `Fetched ${user.name}`)
  }

这将在该组件的每个后续渲染中显示一个祝酒词。而在 React 中,每次渲染父组件时该组件都会重新渲染,因此您可能会看到很多 Toast。

当然,您也可以为此使用useEfect,然后这样做,如果您更喜欢的话。这只是一种风格,有些人更喜欢一种方式,有些人更喜欢另一种方式。

例如,当userAPI.fetchById 可能由于网络问题引发错误时,将调度rejected 操作。

如果只调用 return ,它实际上会返回 undefined 并导致以 undefined 作为有效负载的已完成操作。

【讨论】:

    猜你喜欢
    • 2021-12-01
    • 2021-04-29
    • 2016-07-13
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多