我现在正在学习 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
函数将处理异步调用以获取数据,因为我使用了 createAsyncThunk
。 createAsyncThunk
将创建三个操作:pending
、fulfilled
和 rejected
。并且我在 extraReducers
的 userSlice
中相应地定义了操作。
// 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返回成功加上状态isAuthenticated
和modal
为真,那么我们toggle
(关闭)模态,否则我们记录错误。
但是,即使我看到 Redux DevTool 执行了 user/login/fulfilled
操作,并且 isAuthenticated
状态设置为 true
,模态也不会关闭。
根据我的理解,当 isAuthenticated
thunk 完成时,true
应该已经设置为 login
,所以当我们调用 .then()
方法时我们已经准备好了。是不是因为我通过 isAutheticated
获得了 useSelector
状态,所以它需要一些时间来自我更新?所以我们不能保证当 promise 返回时 React Redux 已经更新了它?成功后有没有更好的方法关闭模态?
感谢您的帮助!您可以找到演示 here。 ('right@gmail.com' + 任何密码 => 成功,其他组合 => 拒绝)
答案 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));
};
这是代码和框:-