我正在建立一个在后端使用rails API并在前端使用React的SPA。用户身份验证按预期工作,我正在从Rails获取用户状态。问题是,当用户登录该应用程序时,我必须手动刷新整个浏览器以更新状态。
// App.js
function userReducer(state, action) {
switch (action.type) {
case "success": {
return {
...state,
id: action.id,
username: action.username,
email: action.email,
logged_in: action.status,
error: ""
};
}
case "fail": {
return {
...state,
id: "",
username: "",
email: "",
logged_in: false,
error: action.error
};
}
default:
return state;
}
}
// set initial state of the logged in user
const intialUserState = {
id: "",
username: "",
email: "",
logged_in: false,
error: ""
};
export const UserStatus = React.createContext(intialUserState);
function App() {
const [userState, dispatch] = useReducer(userReducer, intialUserState);
// fetch the user's data
function fetchLoggedInUserData() {
axios
.get("http://localhost:3000/api/v1/logged_in", { withCredential: true })
.then(response => {
const { id, username, email, logged_in } = response.data;
if (response.status === 200) {
dispatch({
type: "success",
id: id,
username: username,
email: email,
status: logged_in
});
}
})
.catch(error => {
dispatch({ type: "fail", error: error });
});
}
// when the app loads, check if the user is signed in or not.
// if the user is signed in, then store the user's data into the state
useEffect(() => {
fetchLoggedInUserData();
}, []);
return (
<UserStatus.Provider value={{ userState, dispatch }}>
<Router>
<>
<Switch>
<Route path="/admin" component={Admin} />
<Route path="/" render={props => <Public {...props} />} />
</Switch>
</>
</Router>
</UserStatus.Provider>
);
}
上面的代码按预期工作,我可以存储用户状态,如您从代码中看到的那样。问题是当用户单击“提交”按钮时,我想自动存储该用户而不必刷新页面。下面的代码来自Login.js
// Login.js
function signinReducer(state, action) {
switch (action.type) {
case "field": {
return {
...state,
[action.field]: action.value
};
}
case "signin": {
return {
...state,
error: "",
isLoading: true
};
}
case "success": {
return {
...state,
error: "",
isLoading: false
};
}
case "error": {
return {
...state,
isLoading: false,
error: action.error
};
}
}
}
const initialState = {
username: "",
password: "",
isLoading: false,
error: ""
};
function FormMain() {
const [signinState, dispatch] = useReducer(signinReducer, initialState);
const { username, password, isLoading, error } = signinState;
const handleSubmit = async e => {
e.preventDefault();
dispatch({ type: "signin" });
await postUserData();
};
function postUserData() {
axios
.post(
"http://localhost:3000/api/v1/sessions",
{
user: {
username: username,
password: password
}
},
{ withCredentials: true }
)
.then(response => {
if (response.status === 200) {
dispatch({ type: "success" });
}
})
.catch(error => {
// dispatch({ type: "error", error: error.response.data[0] });
});
}
}
我已经从上面的代码中删除了登录表单,因为登录表单太长了。
解决方案可以是通过某种方式将状态从Login.js
转移到App.js
或直接从App.js
更新Login.js
的状态并利用{{1} } useEffect
中,并无需手动刷新浏览器即可更新状态,但是我不知道该怎么做。
答案 0 :(得分:0)
通过在case
上创建新的App.js
并从case
更新Login.js
来解决此问题。
// App.js
function userReducer(state, action) {
switch (action.type) {
case "success": {
return {
...state,
id: action.id,
username: action.username,
email: action.email,
admin: action.admin,
logged_in: action.status,
error: ""
};
}
case "fail": {
return {
...state,
id: "",
username: "",
email: "",
admin: false,
logged_in: false,
error: action.error
};
}
case "globalFetch": {
return {
...state,
id: action.id,
username: action.username,
email: action.email,
admin: action.admin,
logged_in: action.status,
error: ""
};
}
default:
return state;
}
}
您可以看到globalFetch
的情况可以很容易地通过useContext传递给Login.js
,然后根据需要更新该情况。
// Login.js
function postUserData() {
axios
.post(
"http://localhost:3000/api/v1/sessions",
{
user: {
username: username,
password: password
}
},
{ withCredentials: true }
)
.then(response => {
if (response.status === 200) {
const { id, username, email, logged_in, admin } = response.data;
dispatch({ type: "success" });
state.dispatch({
type: "globalFetch",
id: id,
username: username,
email: email,
admin: admin,
status: logged_in
});
}
})
.catch(error => {
// dispatch({ type: "error", error: error.response.data[0] });
});
}
// More code...
}