【问题标题】:Redux Toolkit createAsyncThunk question: state updates after async dispatch call?Redux Toolkit createAsyncThunk 问题:异步调度调用后状态更新?
【发布时间】:2021-08-29 15:21:13
【问题描述】:

我现在正在学习 Redux Thunk。我尝试使用Redux Toolkit中的createAsyncThunk来处理用户登录,但遇到了一些问题。我在这里创建了一个demo('right@gmail.com' + 任何密码 => 成功,其他组合 => 拒绝)。

我创建了一个模式供用户使用reactstrap 输入他们的电子邮件和密码。单击Login 按钮,您将看到表单。

这是我的UserSlice.js 文件:

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { loginFeedback } from "./feedBack";

export const login = createAsyncThunk(
    "user/login",
    async (userInfo, { rejectWithValue }) => {
        try {
            const response = await loginFeedback(userInfo);

            return response.data;
        } catch (err) {
            return rejectWithValue(err);
        }
    }
);

const initialState = {
    isAuthenticated: false,
    isLoading: false,
    user: null,
    error: null,
};

const userSlice = createSlice({
    name: "users",
    initialState,
    reducers: {},
    extraReducers: {
        [login.pending]: (state, action) => {
            state.isLoading = true;
            state.isAuthenticated = false;

        },
        [login.fulfilled]: (state, action) => {
            state.isLoading = false;
            state.isAuthenticated = true;
            state.user = action.payload;
        },
        [login.rejected]: (state, action) => {
            state.isLoading = false;
            state.isAuthenticated = false;
            state.user = [];
            state.error = action.payload.message;
        },
    },
});

export default userSlice.reducer;

所以在我的LoginModal.js文件中,当点击登录按钮时,它会启动表单提交功能handleSubmit()

 const handleSubmit = (e) => {
        e.preventDefault();

        dispatch(login({ email, password }))
        // something else....
}

所以在UserSlice.js 中,login 函数将负责异步调用以获取数据,因为我使用了createAsyncThunkcreateAsyncThunk 将创建三个操作:pendingfulfilledrejected。我在userSliceextraReducers 中相应地定义了这些操作。

// userSlice.js
        [login.pending]: (state, action) => {
            state.isLoading = true;
            state.isAuthenticated = false;

        },
        [login.fulfilled]: (state, action) => {
            state.isLoading = false;
            state.isAuthenticated = true;
            state.user = action.payload;
        },
        [login.rejected]: (state, action) => {
            state.isLoading = false;
            state.isAuthenticated = false;
            state.user = [];
            state.error = action.payload.message;
        },

所以如果loginFeedback 成功,isAuthenticated 状态应该设置为true,如果调用被拒绝,它将设置为false,我们将在表单中显示一条错误消息.

在我的LoginModal.js 中,如果用户身份验证成功,我想关闭模式,如果失败,则显示错误消息。为了将操作视为正常的承诺内容,我在 Redux Toolkits (here) 中找到了这个:

createAsyncThunk 生成的 thunk 将始终返回一个已解决的 Promise,其中包含已完成的操作对象或被拒绝的操作对象,视情况而定。

调用逻辑可能希望将这些操作视为原始承诺内容。 Redux Toolkit 导出一个 unwrapResult 函数,该函数可用于提取已完成操作的有效负载,或抛出错误,或如果可用,则从被拒绝的操作中抛出由 rejectWithValue 创建的有效负载。

所以我将handleSubmit 函数写为:

 const handleSubmit = (e) => {
        e.preventDefault();

        dispatch(login({ email, password }))
            .then(unwrapResult)
            .then(() => {
                if (isAuthenticated && modal) {
                    setEmail("");
                    setPassword("");
                    toggle();
                }
            })
            .catch((error) => {
                setHasError(true);
            });
    };

我们首先unwrapResult,然后如果promise返回成功加上状态isAuthenticatedmodal为真,那么我们toggle(关闭)模式,否则我们记录错误。

但是,即使我看到 Redux DevTool 执行 user/login/fulfilled 操作,模态也不会关闭,并且 isAuthenticated 状态设置为 true

在我的理解中,login thunk 完成时,isAuthenticated 应该已经设置为 true,所以当我们调用 .then() 方法时,我们已经准备好了一切。是不是因为我通过useSelector得到了isAutheticated状态,所以需要一些时间来更新自己?所以我们不能保证当 Promise 返回时 React Redux 已经更新了?成功后有没有更好的方法来关闭模态框?

感谢您的帮助!你可以找到演示here。 ('right@gmail.com' + 任何密码 => 成功,其他组合 => 拒绝)

【问题讨论】:

    标签: javascript reactjs redux react-redux redux-toolkit


    【解决方案1】:

    我认为then 回调中Promise 的返回值确实表示登录操作成功。但这并不意味着你会得到isAuthenticatedtrue,因为你的handleSubmit 会接近之前的isAuthenticatedfalse

    您需要有一个自定义的useEffect,它在isAuthenticated 和您的逻辑需要的其他值上触发。

    以下更改应满足您的需要:-

      const toggle = useCallback(() => {
        setHasError(false);
        setModal((modal) => !modal);
      }, []);
    
      useEffect(() => {
        if (isAuthenticated && modal) {
          setEmail("");
          setPassword("");
          toggle();
        }
      }, [isAuthenticated, modal, toggle]);
    
      const handleSubmit = (e) => {
        e.preventDefault();
        dispatch(login({ email, password }))
          .then(unwrapResult)
          .catch(() => setHasError(true));
      };
    

    这里是代码框:-

    【讨论】:

    • 嗨@Lakshya Thakur!我可以这样解释吗:isAuthenticatedmodal 是独立于组件渲染的副作用,所以我们应该将它们放入 useEffect 钩子以确保 React 在需要时渲染它们?谢谢!
    • 好的,让我试着解释一下。 isAuthenticated 和 modal 是状态值,它们在每次渲染时都会通过 redux 或本地状态发生变化。并且每次渲染还在这里创建一个新的 handleSubmit 函数。问题是您从 handleSubmit 内部分派了 Login 调用,这导致了状态更新和后续渲染。所以它只有在新渲染的 handleSubmit 中才会有最新的 isAuthenticated 值,而不是旧的。访问这些值的最佳位置是在 useEffect 中,它将它们放入其依赖数组中,并在它们发生变化时触发内部回调。
    • 哇,我不知道 React 在每次渲染时都会生成新函数。非常感谢,我明白了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 2021-07-09
    • 2019-01-29
    • 2017-10-23
    • 2022-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多