Reactjs React上下文需要2个状态更新,以便使用者重新呈现
因此,我有一个直接的应用程序,要求您登录以查看仪表板。我已经基于我的auth流,作为回报,它们的流基于 当前,当用户登录时,它调用上下文提供程序中的函数进行登录,该函数使用从服务器检索的用户数据更新上下文的状态。这反映在my context providers下的React-dev工具中,如教师属性所示:Reactjs React上下文需要2个状态更新,以便使用者重新呈现,reactjs,react-hooks,react-router-dom,react-context,Reactjs,React Hooks,React Router Dom,React Context,因此,我有一个直接的应用程序,要求您登录以查看仪表板。我已经基于我的auth流,作为回报,它们的流基于 当前,当用户登录时,它调用上下文提供程序中的函数进行登录,该函数使用从服务器检索的用户数据更新上下文的状态。这反映在my context providers下的React-dev工具中,如教师属性所示: 当上下文状态成功更新后,我使用react router API中的useHistory().push(“dashboard/main”)转到dashboard页面。仪表板是上下文提供程序的使
当上下文状态成功更新后,我使用react router API中的useHistory().push(“dashboard/main”)转到dashboard页面。仪表板是上下文提供程序的使用者,但是当我尝试呈现页面时,teacher值仍然为null,即使React dev tools清楚地显示该值已更新。当我再次登录时,仪表板将成功渲染,因此,最终需要两次上下文更新才能使仪表板反映更改并渲染。请参阅我的以下代码片段(不相关的代码已被编辑): App.js
const App = () => {
return (
<AuthProvider>
<div className="App">
<Switch>
<Route path="/" exact >
<Home setIsFetching={setIsFetching} />
</Route>
<ProtectedRoute path="/dashboard/:page" >
<Dashboard
handleToaster={handleToaster}
/>
</ProtectedRoute>
<ProtectedRoute path="/dashboard">
<Redirect to="/dashboard/main"/>
</ProtectedRoute>
<Route path="*">
<PageNotFound/>
</Route>
</Switch>
<Toaster display={toaster.display} setDisplay={(displayed) => setToaster({...toaster, display: displayed})}>{toaster.body}</Toaster>
</div>
</AuthProvider>
);}
const AuthProvider = ({children}) => {
const auth = useProvideAuth();
return(
<TeacherContext.Provider value={auth}>
{children}
</TeacherContext.Provider>
);};
export const TeacherContext = createContext();
export const useProvideAuth = () => {
const [teacher, setTeacher] = useState(null);
const memoizedTeacher = useMemo(() => ({teacher}), [teacher]);
const signin = (data) => {
fetch(`/api/authenticate`, {method: "POST", body: JSON.stringify(data), headers: JSON_HEADER})
.then(response => Promise.all([response.ok, response.json()]))
.then(([ok, body]) => {
if(ok){
setTeacher(body);
}else{
return {...body};
}
})
.catch(() => alert(SERVER_ERROR));
};
const register = (data) => {
fetch(`/api/createuser`, {method: "POST", body: JSON.stringify(data), headers: JSON_HEADER})
.then(response => Promise.all([response.ok, response.json()]))
.then(([ok, body]) => {
if(ok){
setTeacher(body);
}else{
return {...body};
}
})
.catch(() => alert(SERVER_ERROR));
};
const refreshTeacher = async () => {
let resp = await fetch("/api/teacher");
if (!resp.ok)
throw new Error(SERVER_ERROR);
else
await resp.json().then(data => {
setTeacher(data);
});
};
const signout = () => {
STORAGE.clear();
setTeacher(null);
};
return {
...memoizedTeacher,
setTeacher,
signin,
signout,
refreshTeacher,
register
};
};
export const useAuth = () => {
return useContext(TeacherContext);
};
const ProtectedRoute = ({children, path}) => {
let auth = useAuth();
return (
<Route path={path}>
{
auth.teacher
? children
: <Redirect to="/"/>
}
</Route>
);
};
const Home = ({setIsFetching}) => {
let teacherObject = useAuth();
let history = useHistory();
const handleFormSubmission = (e) => {
e.preventDefault();
const isLoginForm = modalContent === "login";
const data = isLoginForm ? loginObject : registrationObject;
const potentialSignInErrors = isLoginForm ?
teacherObject.signin(data) : teacherObject.register(data);
if(potentialSignInErrors)
setErrors(potentialSignInErrors);
else{
*******MY ATTEMPT TO PUSH TO THE DASHBOARD AFTER USING TEACHEROBJECT.SIGNIN********
history.replace("/dashboard/main");
}
};
};)};
const Dashboard = ({handleToaster}) => {
const [expanded, setExpanded] = useState(true);
return (
<div className={"dashboardwrapper"}>
<Sidebar
expanded={expanded}
setExpanded={setExpanded}
/>
<div className={"dash-main-wrapper"}>
<DashNav/>
<Switch>
<Route path="/dashboard/classroom" exact>
<Classroom handleToaster={handleToaster} />
</Route>
<Route path="/dashboard/progressreport" exact>
<ProgressReport/>
</Route>
<Route path="/dashboard/help" exact>
<Help/>
</Route>
<Route path="/dashboard/goalcenter" exact>
<GoalCenter />
</Route>
<Route path="/dashboard/goalcenter/create" exact>
<CreateGoal />
</Route>
<Route path="/dashboard/profile" exact>
<Profile />
</Route>
<Route path="/dashboard/test" exact>
<Test />
</Route>
<Route path="/dashboard/main" exact>
<DashMain/>
</Route>
</Switch>
</div>
</div>
);
};
ProtectedRoute.js
const App = () => {
return (
<AuthProvider>
<div className="App">
<Switch>
<Route path="/" exact >
<Home setIsFetching={setIsFetching} />
</Route>
<ProtectedRoute path="/dashboard/:page" >
<Dashboard
handleToaster={handleToaster}
/>
</ProtectedRoute>
<ProtectedRoute path="/dashboard">
<Redirect to="/dashboard/main"/>
</ProtectedRoute>
<Route path="*">
<PageNotFound/>
</Route>
</Switch>
<Toaster display={toaster.display} setDisplay={(displayed) => setToaster({...toaster, display: displayed})}>{toaster.body}</Toaster>
</div>
</AuthProvider>
);}
const AuthProvider = ({children}) => {
const auth = useProvideAuth();
return(
<TeacherContext.Provider value={auth}>
{children}
</TeacherContext.Provider>
);};
export const TeacherContext = createContext();
export const useProvideAuth = () => {
const [teacher, setTeacher] = useState(null);
const memoizedTeacher = useMemo(() => ({teacher}), [teacher]);
const signin = (data) => {
fetch(`/api/authenticate`, {method: "POST", body: JSON.stringify(data), headers: JSON_HEADER})
.then(response => Promise.all([response.ok, response.json()]))
.then(([ok, body]) => {
if(ok){
setTeacher(body);
}else{
return {...body};
}
})
.catch(() => alert(SERVER_ERROR));
};
const register = (data) => {
fetch(`/api/createuser`, {method: "POST", body: JSON.stringify(data), headers: JSON_HEADER})
.then(response => Promise.all([response.ok, response.json()]))
.then(([ok, body]) => {
if(ok){
setTeacher(body);
}else{
return {...body};
}
})
.catch(() => alert(SERVER_ERROR));
};
const refreshTeacher = async () => {
let resp = await fetch("/api/teacher");
if (!resp.ok)
throw new Error(SERVER_ERROR);
else
await resp.json().then(data => {
setTeacher(data);
});
};
const signout = () => {
STORAGE.clear();
setTeacher(null);
};
return {
...memoizedTeacher,
setTeacher,
signin,
signout,
refreshTeacher,
register
};
};
export const useAuth = () => {
return useContext(TeacherContext);
};
const ProtectedRoute = ({children, path}) => {
let auth = useAuth();
return (
<Route path={path}>
{
auth.teacher
? children
: <Redirect to="/"/>
}
</Route>
);
};
const Home = ({setIsFetching}) => {
let teacherObject = useAuth();
let history = useHistory();
const handleFormSubmission = (e) => {
e.preventDefault();
const isLoginForm = modalContent === "login";
const data = isLoginForm ? loginObject : registrationObject;
const potentialSignInErrors = isLoginForm ?
teacherObject.signin(data) : teacherObject.register(data);
if(potentialSignInErrors)
setErrors(potentialSignInErrors);
else{
*******MY ATTEMPT TO PUSH TO THE DASHBOARD AFTER USING TEACHEROBJECT.SIGNIN********
history.replace("/dashboard/main");
}
};
};)};
const Dashboard = ({handleToaster}) => {
const [expanded, setExpanded] = useState(true);
return (
<div className={"dashboardwrapper"}>
<Sidebar
expanded={expanded}
setExpanded={setExpanded}
/>
<div className={"dash-main-wrapper"}>
<DashNav/>
<Switch>
<Route path="/dashboard/classroom" exact>
<Classroom handleToaster={handleToaster} />
</Route>
<Route path="/dashboard/progressreport" exact>
<ProgressReport/>
</Route>
<Route path="/dashboard/help" exact>
<Help/>
</Route>
<Route path="/dashboard/goalcenter" exact>
<GoalCenter />
</Route>
<Route path="/dashboard/goalcenter/create" exact>
<CreateGoal />
</Route>
<Route path="/dashboard/profile" exact>
<Profile />
</Route>
<Route path="/dashboard/test" exact>
<Test />
</Route>
<Route path="/dashboard/main" exact>
<DashMain/>
</Route>
</Switch>
</div>
</div>
);
};
Dashboard.js
const App = () => {
return (
<AuthProvider>
<div className="App">
<Switch>
<Route path="/" exact >
<Home setIsFetching={setIsFetching} />
</Route>
<ProtectedRoute path="/dashboard/:page" >
<Dashboard
handleToaster={handleToaster}
/>
</ProtectedRoute>
<ProtectedRoute path="/dashboard">
<Redirect to="/dashboard/main"/>
</ProtectedRoute>
<Route path="*">
<PageNotFound/>
</Route>
</Switch>
<Toaster display={toaster.display} setDisplay={(displayed) => setToaster({...toaster, display: displayed})}>{toaster.body}</Toaster>
</div>
</AuthProvider>
);}
const AuthProvider = ({children}) => {
const auth = useProvideAuth();
return(
<TeacherContext.Provider value={auth}>
{children}
</TeacherContext.Provider>
);};
export const TeacherContext = createContext();
export const useProvideAuth = () => {
const [teacher, setTeacher] = useState(null);
const memoizedTeacher = useMemo(() => ({teacher}), [teacher]);
const signin = (data) => {
fetch(`/api/authenticate`, {method: "POST", body: JSON.stringify(data), headers: JSON_HEADER})
.then(response => Promise.all([response.ok, response.json()]))
.then(([ok, body]) => {
if(ok){
setTeacher(body);
}else{
return {...body};
}
})
.catch(() => alert(SERVER_ERROR));
};
const register = (data) => {
fetch(`/api/createuser`, {method: "POST", body: JSON.stringify(data), headers: JSON_HEADER})
.then(response => Promise.all([response.ok, response.json()]))
.then(([ok, body]) => {
if(ok){
setTeacher(body);
}else{
return {...body};
}
})
.catch(() => alert(SERVER_ERROR));
};
const refreshTeacher = async () => {
let resp = await fetch("/api/teacher");
if (!resp.ok)
throw new Error(SERVER_ERROR);
else
await resp.json().then(data => {
setTeacher(data);
});
};
const signout = () => {
STORAGE.clear();
setTeacher(null);
};
return {
...memoizedTeacher,
setTeacher,
signin,
signout,
refreshTeacher,
register
};
};
export const useAuth = () => {
return useContext(TeacherContext);
};
const ProtectedRoute = ({children, path}) => {
let auth = useAuth();
return (
<Route path={path}>
{
auth.teacher
? children
: <Redirect to="/"/>
}
</Route>
);
};
const Home = ({setIsFetching}) => {
let teacherObject = useAuth();
let history = useHistory();
const handleFormSubmission = (e) => {
e.preventDefault();
const isLoginForm = modalContent === "login";
const data = isLoginForm ? loginObject : registrationObject;
const potentialSignInErrors = isLoginForm ?
teacherObject.signin(data) : teacherObject.register(data);
if(potentialSignInErrors)
setErrors(potentialSignInErrors);
else{
*******MY ATTEMPT TO PUSH TO THE DASHBOARD AFTER USING TEACHEROBJECT.SIGNIN********
history.replace("/dashboard/main");
}
};
};)};
const Dashboard = ({handleToaster}) => {
const [expanded, setExpanded] = useState(true);
return (
<div className={"dashboardwrapper"}>
<Sidebar
expanded={expanded}
setExpanded={setExpanded}
/>
<div className={"dash-main-wrapper"}>
<DashNav/>
<Switch>
<Route path="/dashboard/classroom" exact>
<Classroom handleToaster={handleToaster} />
</Route>
<Route path="/dashboard/progressreport" exact>
<ProgressReport/>
</Route>
<Route path="/dashboard/help" exact>
<Help/>
</Route>
<Route path="/dashboard/goalcenter" exact>
<GoalCenter />
</Route>
<Route path="/dashboard/goalcenter/create" exact>
<CreateGoal />
</Route>
<Route path="/dashboard/profile" exact>
<Profile />
</Route>
<Route path="/dashboard/test" exact>
<Test />
</Route>
<Route path="/dashboard/main" exact>
<DashMain/>
</Route>
</Switch>
</div>
</div>
);
};
const Dashboard=({handleToaster})=>{
const[expanded,setExpanded]=useState(true);
返回(
);
};
让我知道是否有任何突出之处会阻止我的仪表板第一次使用更新的上下文值进行渲染,而不必更新两次。如果您需要对我的代码有更多的了解,或者如果我遗漏了什么,请务必让我知道——我对这方面还相当陌生。此外,任何关于我的应用程序结构的指针都将非常感谢,因为这是我的第一个React项目。谢谢。我想问题出在
handleFormSubmission
函数中:
const handleFormSubmission = (e) => {
e.preventDefault();
const isLoginForm = modalContent === "login";
const data = isLoginForm ? loginObject : registrationObject;
const potentialSignInErrors = isLoginForm ?
teacherObject.signin(data) : teacherObject.register(data);
if(potentialSignInErrors)
setErrors(potentialSignInErrors);
else{
history.replace("/dashboard/main");
}
};
调用teacherObject.signin(数据)
或teacherObject.register(数据)
,然后依次更改历史记录状态
问题是,在调用历史记录.replace
之前,您无法确定教师
状态是否已更新
我制作了一个简化版的home组件,以举例说明如何解决这个问题
function handleSignin(auth) {
auth.signin("data...");
}
const Home = () => {
const auth = useAuth();
useEffect(() => {
if (auth.teacher !== null) {
// state has updated and teacher is defined, do stuff
}
}, [auth]);
return <button onClick={() => handleSignin(auth)}>Sign In</button>;
};
函数handleSignin(auth){
授权签名(“数据…”);
}
常量Home=()=>{
const auth=useAuth();
useffect(()=>{
如果(auth.teacher!==null){
//状态已更新,教师已定义,请执行操作
}
},[auth]);
返回handleSignin(auth)}>登录;
};
因此,当
auth
更改时,请检查teacher
是否有值,并对其进行处理。如果有一个简单的代码沙盒来调试代码,那就太好了。如果您能完成它,非常感谢您花时间帮助我。奇怪的是,我的大脑在午夜后就不能正常工作了。我刚刚在Home.js中添加了useEffect,如果上下文教师值存在,它将重定向到仪表板。