React Context 和 Provider 数据没有按预期传递

Posted

技术标签:

【中文标题】React Context 和 Provider 数据没有按预期传递【英文标题】:React Context and Provider data doesn't get passed down as expected 【发布时间】:2022-01-12 01:12:30 【问题描述】:

我正在尝试更新另一个组件正在使用的对象。

我的背景:

import React, Component, createContext from "react";
import jwt from 'jsonwebtoken';

const LOCAL_STORAGE_AUTH_TOKEN = 'authToken';

interface AuthenticatedUser 
    username?: string;
    guildName?: string;
    guildId?: string;


interface AuthContextType 
    authenticated: boolean; // to check if authenticated or not
    user: AuthenticatedUser; // store user details
    token: string; //jwt token
    refreshToken: string; //jwt refresh token
    handleAuthentication: (username: string, password: string) => Promise<void>; // handle login process
    logout: () => Promise<void>; // log out the user


export const AuthContext = createContext<AuthContextType>(
    authenticated: false, // to check if authenticated or not
    user: , // store all the user details
    token: '', // store all the user details
    refreshToken: '', //jwt refresh token
    handleAuthentication: (username: string, password: string): Promise<void> =>
        Promise.resolve(), // handle login process
    logout: (): Promise<void> => Promise.resolve(), // logout the user
);
AuthContext.displayName = 'AuthContext';

export class AuthProvider extends Component 
    state = 
        authenticated:false,
        user: ,
        token:'',
        refreshToken:''
    ;
    
    constructor(props: any) 
        super(props);
        
        const token = window.localStorage.getItem(LOCAL_STORAGE_AUTH_TOKEN) || '';
        const jwtData = jwt.decode(token);
        
        let user = ;
        let authenticated = false;
        let refreshToken = '';
        if(jwtData && typeof jwtData !== 'string')
            authenticated = true;
            user =  
                username: jwtData.data?.username || '',
                // TODO: Add the other sources too
            
        
        
        this.state = 
            authenticated,
            user,
            token,
            refreshToken
        
    
    
    handleAuthentication = async (username: string, password: string):Promise<void> => 
        fetch(process.env.REACT_APP_API_ENDPOINT  + "users/login", 
            method:"POST",
            headers:  "Content-Type": "application/json" ,
            body: JSON.stringify(username:username, password:password)
        )
            .then(response => response.json())
            .then((data) => 
                /*
                        This data is coming back correctly
                */
                const user = 
                    username: data?.user.username,
                    guildName: data?.user.guild_information[0].guildName,
                    guildId: "123123"
                
                
                this.setState(
                    authenticated: true,
                    user: user,
                    token: data?.token,
                    refreshToken: data?.refreshToken
                )
                
                window.localStorage.setItem(LOCAL_STORAGE_AUTH_TOKEN, data?.token)
                //TODO: Also save token in localstorage
            )
            .catch((err) => 
                console.log(err)
            )
    
    
    logout = async ():Promise<void> => 
        //TODO: Log out the current user
    
    
    render() 
        const authProviderValue =  
            ...this.state,
            handleAuthentication: this.handleAuthentication,
            logout: this.logout
        
        
        return (
            <AuthContext.Provider value=authProviderValue>
                this.props.children
            </AuthContext.Provider>
        )
        
    

以及我使用它的 App 组件:

import React, useContext from "react";
import  useStyles from "./style";
import AuthContext, AuthProvider from "../../context/UserContext";
import RoutesProvider from "./Routes";
import Login from "../Login";

export default function App() 
  const classes = useStyles();
  const  user  = useContext(AuthContext)
  
  return (
    <AuthProvider>
        typeof user.username !== "undefined" ? (
          <div className=classes.content>
            <RoutesProvider />
          </div>
        ):(
          <Login />
        )
    </AuthProvider>
  )

还按照建议将 App 组件包装在 AuthProvider 中:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import  SnackbarProvider  from 'notistack';
import  AuthProvider from "./context/UserContext";
import './styles/index.css'

ReactDOM.render(
  <React.StrictMode>
    <AuthProvider>
      <SnackbarProvider maxSnack=3>
        <App />
      </SnackbarProvider>
    </AuthProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

如果我在 AuthContext 中更改它,它确实会采用初始值。但是更新后它不会在 App 组件中更新。我有点困惑为什么这不能按预期工作。如果有人知道是什么原因造成的(或者我做错了),请告诉我。目前调试 5 小时...

【问题讨论】:

【参考方案1】:

如果我正确理解了您的问题,您是在问:

“为什么当你的 handleAuthentication 方法成功时你的用户常量没有更新”

答案是因为您在提供者之外初始化用户常量。

在 index.tsx 中使用 AuthProvider,而不是像这样的 App 组件:

<AuthProvider>
   <App />
</AuthProvider>

或者将您的上下文逻辑转移到子组件中

【讨论】:

我刚刚添加了。这确实适用于 handleAuthentication 方法,但只有在刷新之后才能从 localStorage 获取 authToken。 从您的 App 组件中删除身份验证提供程序 你真是天赐良机。这完美无缺。我也看到出了什么问题并现在理解它,至少在某种程度上:D 非常感谢您

以上是关于React Context 和 Provider 数据没有按预期传递的主要内容,如果未能解决你的问题,请参考以下文章

React Context 和 Provider 数据没有按预期传递

06.React组件进阶(二)Context

使用React Context进行状态管理(五)Provider与Consumer匹配

React中的connect使用(Provider+Consumer例子 以及 contextType+this.context例子)

react

React Context Provider 所有子级重新渲染