Redux Toolkit createAsyncThunk 问题:异步调度调用后状态更新?

Redux Toolkit createAsyncThunk question: state updates after async dispatch call?

我正在学习 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。并且我在 userSlice.

extraReducers 中相应地定义了操作
// 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中,如果用户认证成功,我想关闭模式,如果失败,则显示错误信息。为了像正常的 promise 内容一样对待 action,我在 Redux Toolkits (here) 中找到了这个:

The thunks generated by createAsyncThunk will always return a resolved promise with either the fulfilled action object or rejected action object inside, as appropriate.

The calling logic may wish to treat these actions as if they were the original promise contents. Redux Toolkit exports an unwrapResult function that can be used to extract the payload of a fulfilled action or to throw either the error or, if available, payload created by rejectWithValue from a rejected action.

所以我将 handleSubmit 函数写为:

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

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

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

但是,即使我看到 Redux DevTool 执行了 user/login/fulfilled 操作并且 isAuthenticated 状态设置为 true.[=55=,模态框也没有关闭]

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

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

我认为 then 回调中 Promise 的 return 值确实表示登录操作成功。但这并不意味着您将获得 isAuthenticated 作为 true,因为您的 handleSubmit 将收盘价超过 isAuthenticated 的先前值 false

您需要自定义 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));
  };

这是代码和框:-