【问题标题】:REACT - useContext state sharing sync issueREACT - useContext 状态共享同步问题
【发布时间】:2020-07-02 14:52:56
【问题描述】:

目标:-

我希望用户只有在登录时才能看到他/她的订单。所以我使用 AuthContext 对登录数据+令牌的用户进行状态管理。

问题:-

当我将令牌从 AuthContext 传递给子组件时,AuthContext 需要一些时间来与后端验证令牌,同时子组件的逻辑中断。

子组件(使用状态/令牌):

const MyOrders = () => {
    const { userData } = useContext(AuthContext);
    const history = useHistory();
    if (!userData.token) {      // This redirects the user back to the home page immediately
        history.push("/");      // because the token hasn't been passed yet when
                                // the component is loaded
    };
    const getOrders = async () => {
        const url = 'http://localhost:5000/api/orders';
        try {
            const res = await axios.get(url, {
                headers: {
                    'x-auth-token': userData.token
                }
            });
            console.log(res.data);
        } catch (err) {
            console.log(err.response);
        }
    };
    useEffect(() => {
        if (userData.token) getOrders();
    }, []);

解决方法(安全吗????)

const MyOrders = () => {
    const token = localStorage.getItem('auth-token');  // Use localStorage token directly instead of 
                                                    // validating the token first (from AuthContext)??
    const history = useHistory();
    if (!token) {      
        history.push("/");                                      
    };

    const getOrders = async () => {
        const url = 'http://localhost:5000/api/orders';
        try {
            const res = await axios.get(url, {
                headers: {
                    'x-auth-token': token
                }
            });
            console.log(res.data);
        } catch (err) {
            console.log(err.response);
        }
    };

    useEffect(() => {
        if (userData.token) getOrders();
    }, []);

父组件 (AuthContext) : // 万一有人需要

const AuthContextProvider = (props) => {
    const [userData, setUserData] = useState({
        token: undefined,
        user: undefined
    });
    //Need to check if user is logged in
    //every time the App is rendered
    useEffect(() => {
        const checkLoggedIn = async () => {
            const url = "http://localhost:5000/api/users/token";
            let token = localStorage.getItem("auth-token");
            //when user is not logged in
            if(token === null) {
                localStorage.setItem("auth-token", "");
                token = "";
            }
            //need to validate the token if it exists
            const tokenResponse = await axios.post(url, null, {
                headers: { "x-auth-token": token }
            });
            //if token is valid, collect user data 
            if(tokenResponse.data){
                console.log(tokenResponse);
                setUserData({
                    token,
                    user: tokenResponse.data,

                });
            } else {
                localStorage.setItem("auth-token", "");
                token = "";
            };
        };
        checkLoggedIn();                
    }, []);

【问题讨论】:

    标签: reactjs react-native react-redux react-router


    【解决方案1】:

    也许你可以尝试在 history.push("/") 之后提前返回,这样其余的逻辑就不会执行了

    const MyOrders = () => {
        const { userData } = useContext(AuthContext);
        const history = useHistory();
        if (!userData.token) {      // This redirects the user back to the home page immediately
            history.push("/");  
            return (null)
        };
        const getOrders = async () => {
            const url = 'http://localhost:5000/api/orders';
            try {
                const res = await axios.get(url, {
                    headers: {
                        'x-auth-token': userData.token
                    }
                });
                console.log(res.data);
            } catch (err) {
                console.log(err.response);
            }
        };
        useEffect(() => {
            if (userData.token) getOrders();
        }, []);
    

    【讨论】:

    • 这与我想要的相反。我不希望它返回,我想将订单显示给我无法登录的用户。因为即使用户已登录,useContext 也会在延迟后将令牌提供给该子组件,与此同时,用户会被路由回主页。
    • 您可以删除 history.push('/') 然后当令牌从 AuthContext 更新时 MyOrder 组件将再次呈现。即使你可以在那里显示“正在加载..”文本
    【解决方案2】:

    我建议您尝试在应用程序的第一页中检索保存的令牌。 在函数上使用“等待”。完成尝试从 localStorage 检索后, 如果令牌不存在,您应该导航到登录,否则导航到主页。 在这种情况下,如果您有令牌,您将不会有未定义的令牌,

    但是如果令牌以某种方式未定义怎么办?如果用户尝试使用未定义的令牌获取,则需要从服务器发送未经授权的错误,然后在客户端捕获错误,检查消息和收到的类型,如果出现授权错误,导航到主页页面。

    这是一个有点复杂的答案,但它肯定会解决您的问题,它会使其代码更加简洁和可靠。

    【讨论】:

      【解决方案3】:

      您可以为您的用户数据添加加载,如果设置用户数据失败,则将加载设置为 false,您可以通过从 react-router 返回一个 Redirect 组件来按您的顺序重定向。

      const AuthContext = React.createContext();
      const AuthContextProvider = ({ children }) => {
        const [userData, setUserData] = React.useState({
          token: undefined,
          user: undefined,
          loading: true,
        });
        React.useEffect(() => {
          setTimeout(
            () =>
              setUserData((userData) => ({
                ...userData, //any other data that may be there
                token: 123,
                user: 'Hello world',
                loading: false, //not loading anymore
              })),
            5000 //wait 5 seconds to set user data
          );
        }, []);
        return (
          <AuthContext.Provider value={userData}>
            {children}
          </AuthContext.Provider>
        );
      };
      const App = () => {
        const { loading, ...userData } = React.useContext(
          AuthContext
        );
        if (!loading && !userData.token) {
          //snippet does not have redirect
          //  but if you have react-router you
          //  can do this
          //return <Redirect to="/" />;
        }
        React.useEffect(() => {}, []);
        return loading ? (
          'loading...'
        ) : (
          <pre>{JSON.stringify(userData, undefined, 2)}</pre>
        );
      };
      ReactDOM.render(
        <AuthContextProvider>
          <App />
        </AuthContextProvider>,
        document.getElementById('root')
      );
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
      <div id="root"></div>

      【讨论】:

        猜你喜欢
        • 2020-12-30
        • 2019-10-22
        • 2023-01-19
        • 2017-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-31
        • 2019-02-13
        相关资源
        最近更新 更多