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

时间:2021-06-13 12:04:55

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

我现在正在学习 Redux Thunk。我尝试使用 createAsyncThunk 中的 Redux Toolkit 来处理用户登录,但遇到了一些问题。我在这里创建了一个 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。并且我在 extraReducersuserSlice 中相应地定义了操作。

// 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 将始终返回一个已解决的承诺,其中包含已完成的操作对象或被拒绝的操作对象(视情况而定)。

<块引用>

调用逻辑可能希望将这些操作视为原始承诺内容。 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,模态也不会关闭。

enter image description here

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

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

1 个答案:

答案 0 :(得分:0)

我认为 Promise 回调中 then 的返回值确实表示登录操作成功。但这并不意味着您将获得 isAuthenticated 作为 true,因为您的 handleSubmit 会关闭 isAuthenticated 的先前值 false。< /p>

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

这是代码和框:-

Edit react-modal (forked) - stackoverflow