【问题标题】:React : Unable to set context state in app.js using useContext hook反应:无法使用 useContext 钩子在 app.js 中设置上下文状态
【发布时间】:2020-10-20 10:18:13
【问题描述】:

我无法在我的 app.js 中设置上下文状态,我在其中得到了空值,但可以在子组件中访问它。

我想在用户访问页面时在 app.js 中设置上下文状态,以便我可以在整个应用程序中使用它,例如根据用户是否登录显示不同的标题

请求的沙盒 URL -> https://codesandbox.io/s/quizzical-snyder-2qghj?file=/src/App.js

我关注https://upmostly.com/tutorials/how-to-use-the-usecontext-hook-in-react

app.js

// import installed dependencies
import React, { useEffect, useContext } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';

// import custom contexts
import { AuthContext, AuthContextProvider } from './contexts/auth/AuthContext';

// import pages
import Homepage from './pages/homepage/Homepage';

// import components
import Footer from './components/footer/Footer';
import Header from './components/header/Header';

export default function App() {
    const [authState, setAuthState] = useContext(AuthContext);

    useEffect(() => {
        console.log(authState); // prints *{}*
        console.log(setAuthState); // prints *() => {}*
        const token = localStorage.getItem('token');
        const tokenIsExpired = parseInt(localStorage.getItem('tokenIsExpired'));

        if (!tokenIsExpired && token.length) {
            setAuthState({
                userIsLoggedin: true,
                fName: 'test fname',
                lName: 'test lname',
                userName: 'testname'
            });
        } else {
            setAuthState({
                userIsLoggedin: false,
                fName: '',
                lName: '',
                userName: ''
            });
        }

        if (tokenIsExpired) {
            localStorage.setItem('token', '');
        }
    }, [authState, setAuthState]);

    return (
        <Router>
            <AuthContextProvider value={[authState, setAuthState]}>
                <div className='App'>
                    <Header />
                    <Switch>
                        <Route exact path='/'>
                            <Homepage />
                        </Route>
                    </Switch>
                    <Footer />
                </div>
            </AuthContextProvider>
        </Router>
    );
}

AuthContext.js

import React, { useState, createContext } from 'react';

const AuthContext = createContext([{}, () => {}]);

const AuthContextProvider = (props) => {
    const [authState, setAuthState] = useState({
        userIsLoggedin: false,
        fName: '',
        lName: '',
        userName: ''
    });
    return (
        <AuthContext.Provider value={[authState, setAuthState]}>
            {props.children}
        </AuthContext.Provider>
    );
};

export { AuthContext, AuthContextProvider };

UseAuthCONtext.js

import { useContext } from 'react';
import { AuthContext } from './AuthContext';

const useAuthContext = () => {
    const [authState, setAuthState] = useContext(AuthContext);

    const login = (loginDetails) => {
        setAuthState({
            userIsLoggedin: true,
            fName: 'test fname',
            lName: 'test lname',
            userName: 'testname'
        });
    };

    const logout = () => {
        setAuthState({
            userIsLoggedin: false,
            fName: '',
            lName: '',
            userName: ''
        });
    };

    return { login, logout };
};

export default useAuthContext;

Header.js

// import installed dependencies
import React, { useContext, useEffect } from 'react';

// import components
import LoggedOutHeader from './logged-out-header/LoggedOutHeader';
import LoggedInHeader from './logged-in-header/LoggedInHeader';

// import custom contexts
import { AuthContext } from '../../contexts/auth/AuthContext';

const Header = () => {
    const [authState, setAuthState] = useContext(AuthContext);
    console.log(authState);  //prints *{userIsLoggedin: false, fName: "", lName: "", userName: ""}*
    console.log(setAuthState); //prints *ƒ dispatchAction(fiber, queue, action) {...*
    const header = authState.isUserLoggedIn ? (
        <LoggedInHeader />
    ) : (
        <LoggedOutHeader />
    );

    return header;
};

export default Header;

【问题讨论】:

  • 规则101永远不要在useEffect的依赖数组中添加setter函数
  • 请创建沙盒并分享

标签: javascript reactjs react-hooks react-context


【解决方案1】:

您可以在index.js 中使用上下文提供程序。

ReactDOM.render(
    <AuthContextProvider>
        <App />
    </AuthContextProvider>, 
    document.getElementById('root')
)

【讨论】:

    【解决方案2】:

    您将value 传递给AuthContextProvider,这似乎是您想要使用的value,但您没有使用它。

    // value not used inside `AuthContextProvider`
    <AuthContextProvider value={[authState, setAuthState]}>
    

    应该是:

    const AuthContextProvider = (props) => {
      return (
        <AuthContext.Provider value={props.value}>
          {props.children}
        </AuthContext.Provider>
      );
    };
    

    【讨论】:

    【解决方案3】:

    您正在使用 App 组件中的上下文,该上下文未包含在 AuthContextProvider 中。在这种情况下,App 组件中的 useContext 调用不会返回提供给 AuthContextProvider 的值,而是会返回提供给 createContext 调用的“默认”值。

    您需要将 App 组件中的这些逻辑推迟到 AuthContextProvider 中的子组件。

    createContext api的注释: defaultValue 参数仅在组件树中没有匹配的 Provider 时使用。这有助于在不包装组件的情况下单独测试组件。注意:将 undefined 作为 Provider 值传递不会导致消费组件使用 defaultValue。

    【讨论】:

      猜你喜欢
      • 2020-12-03
      • 2020-11-30
      • 1970-01-01
      • 1970-01-01
      • 2020-05-12
      • 2020-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多