在重定向之前需要反应上下文来更新

Posted

技术标签:

【中文标题】在重定向之前需要反应上下文来更新【英文标题】:Need react context to update before redirect 【发布时间】:2021-05-29 03:47:13 【问题描述】:

我有一个UserContext,它在App 呈现时设置。 App 从服务器检索当前用户,然后在提供程序中设置用户上下文。

所以每当我导航到一个链接时,App 会呈现,从服务器获取当前用户并设置它。这允许所有孩子都可以访问该用户。

<Redirect> 的问题

但是我在使用<Redirect> 时遇到了问题。

如果用户在服务器上更新,那么App 需要重新渲染才能获得更新的用户对象。

但是在重定向时 App 不会重新渲染,这会导致用户上下文过时,直到用户刷新页面以重新渲染 App

示例:登录查看您的个人资料

在下面的代码中,我有一个登录按钮。当用户登录时,页面会重定向到他们的个人资料。

但是即使用户在服务器上成功登录,用户上下文也没有更新。这是因为重定向不会重新渲染App

有没有办法重定向到重新渲染应用程序或其他解决方案?

代码

相关代码如下。

完整代码可在repo here 上找到。下载并运行npm inpm start,然后在调试器中选择并播放Compounded Server/React 或运行node currentUserServer/server.js 以在不使用调试器工具的情况下启动服务器。

前端

App.js

import React,  useEffect, useContext, useState  from "react";
import  UserContext  from "./contexts/UserContext";
import  BrowserRouter as Router, Switch, Route  from "react-router-dom";

import Login from "./Login";
import Profile from "./Profile";

const currentUser = async () => 
  const user = await fetch("/users/current", ).then(async (res) => 
    const userJson = await res.json();
    return userJson;
  );
  return user;
;
export default function App() 
  const [user, setUser] = useState(null);

  useEffect(() => 
    currentUser().then((user) => 
      setUser(user);
    );
  , []);

  return (
    <Router>
      <div className="App">
        <UserContext.Provider value=user>
          <Switch>
            <Route path="/profile">
              <Profile />
            </Route>
            <Route path="/">
              <Login />
            </Route>
          </Switch>
        </UserContext.Provider>
      </div>
    </Router>
  );

登录.js

import React,  useContext, useState  from "react";
import  Redirect  from "react-router-dom";
import  UserContext  from "./contexts/UserContext";

export default function Login() 
  const [next, setNext] = useState(false);

  const currentUser = useContext(UserContext);

  return (
    <div>
      Logged In:" "
      !currentUser || currentUser.message === "not logged in"
        ? "No One"
        : currentUser.username" "
      <br></br>
      <button
        onClick=() => 
          fetch("/login",  method: "POST" ).then((res) => 
            if (res.status === 201) setNext(true);
          );
        
      >
        Login
      </button>
      <button
        onClick=() => 
          fetch("/logout",  method: "DELETE" );
        
      >
        Logout
      </button>
      next && <Redirect to="/profile" />
    </div>
  );

Profile.js

 import React,  useContext  from "react";
import  UserContext  from "./contexts/UserContext";

export default function Profile() 
  const currentUser = useContext(UserContext);

  return (
    <div>
      currentUser && !currentUser.message
        ? "You're logged in and can edit your profile."
        : "You're not logged in."
    </div>
  );

【问题讨论】:

仅供参考: 使用async/await,您可以获取just three lines 中的用户。 【参考方案1】:

我找到了两种解决方案。

#1 快速修复但效果不佳:setRefresh

这可行,但似乎取消了使用上下文的全部意义,因为我们必须向下传递状态。

我在App.js 上创建了一个refresh 状态,触发useEffect

  useEffect(() => 
    if (refresh) 
      currentUser().then((user) => 
        setUser(user);
        setRefresh(false);
      );
    
  , [refresh]);

现在我可以将setRefresh 作为道具传递给Login 以触发useEffect 再次运行。

Full code here

#2 更好! setUsergetUser 在上下文中

这有点像上面的解决方案,但它使您仍然可以利用上下文。您将 setUsergetUser 方法放在上下文中 - 受此 answer 启发。

app.js 中,我们创建了一个user 状态。我们将UserContext 的值设置为一个对象。该对象包含usersetUser。我们还传入了getUser,这是一个从服务器请求当前用户的函数。

export default function App() 
  const [user, setUser] = useState(null);
  const getUser = async () => 
    const user = await fetch("/users/current", ).then(async (res) => 
      const userJson = await res.json();
      return userJson;
    );
    return user;
  ;
  const value =  user, setUser, getUser ;

这里将对象传递给提供者。

        <UserContext.Provider value=value>

现在,只要有UserContext 消费者,我们就可以访问usersetUsergetUser

const  user, setUser, getUser  = useContext(UserContext);

我们可以使用getUser ping 当前用户的服务器。然后将该结果设置为user

setUser(await getUser());
export default function Login(props) 
  const [next, setNext] = useState(false);

  const  user, setUser, getUser  = useContext(UserContext);

  return (
    <div>
      Logged In:" "
      !user || user.message === "not logged in" ? "No One" : user.username" "
      <br></br>
      <button
        onClick=() => 
          fetch("/login",  method: "POST" ).then(async (res) => 
            if (res.status === 201) 
              setUser(await getUser());
              setNext(true);
            
          );
        
      >
        Login
      </button>

Full Code here

【讨论】:

以上是关于在重定向之前需要反应上下文来更新的主要内容,如果未能解决你的问题,请参考以下文章

即时反应上下文 api 更新状态

反应上下文(钩子)不更新所有引用

如何将反应上下文与反应导航一起使用

React - 每当更新反应上下文中的状态时如何调用函数?

来自库的反应上下文未在消费者中更新

React Hooks with React Router v4 - 我如何重定向到另一个路由?