刷新页面时,react hooks上下文状态未定义

问题描述

我正在创建一个社交媒体应用程序,当我与用户登录时,我将令牌存储在本地存储中,还将用户的信息放在上下文中,以便在其工作及其之后的多个组件中使用它,但是当我刷新页面时,数据消失并且上下文未定义。我在做什么错了?

我的App.js

function App() {
   const [userData,setUserData] = useState({
      name: "",lastname: "",username: "",email: "",});

   return (
      <Router>
          <userContext.Provider value={{ userData,setUserData }}>
             <div className="app">
             <Switch>
                <Route exact path="/" component={Login} />
                <Route path="/register" component={Register} />
                <Route path="/home" component={Home} />
                <Route path="/profile" component={Profile} />
             </Switch>
             </div>
          </userContext.Provider>
      </Router>
     );
   }

我的Login.js

function Login() {
const history = useHistory()
const {userData,setUserData} = useContext(userContext)
const [loginUser,setLoginUser] = useState({
    email: '',password: ''
})
const [error,setError] = useState()

const {email,password} = loginUser
const handleOnChange = (e) => {
    setLoginUser({ ...loginUser,[e.target.name]: e.target.value})
}

const handleOnSubmit = (e) => {
    e.preventDefault()
    axios.post("http://localhost:5000/login",loginUser)
    .then((res) => {
        setUserData({
            name: res.data.user.name,lastname: res.data.user.lastname,username: res.data.user.username,email: res.data.user.email,})        
        localStorage.setItem("auth-token",res.data.token)
        history.push("/home")   
    })
    .catch(err => {
         err.response.data.msg && setError(err.response.data.msg)
    })  
}  

return (
    <div className="login">
        <div>
            <form onSubmit={handleOnSubmit} className="login-form">
            {error ? <h4 className='login-error'>{error}</h4> : ""}
                <input type="email" placeholder="Email or username" value={email} name='email' onChange={e => handleOnChange(e)}/>
                <input type="password" placeholder="Password" value={password} name='password' onChange={e => handleOnChange(e)}/>
                <Button type="submit" variant="contained">Log in</Button>
                <div></div>
                <p>Don't have an account? <Link to="/register">Click here</Link></p>
            </form>
            <h1>Raise your word <TwitterIcon className="login-right-icon"/></h1> 
        </div>
    </div>
)
}

我的Home.js在刷新的地方会丢失上下文中的数据

function Home() {
const {userData,setUserData} = useContext(userContext)
const [posts,setPost] = useState([])
const [createPost,setCreatePost] = useState('')
const handletoken = () => {
    localStorage.removeItem('auth-token')
}

const token = localStorage.getItem("auth-token");

const handleOnSubmit = (e) => {
    e.preventDefault()
    axios.post('http://localhost:5000/posts',{textOfThePost: createPost},{
        headers: { 'auth-token': token },})
    .then((res) => {setCreatePost("")})
}

useEffect(() => {
    axios.get('http://localhost:5000/posts')
    .then(res => {
        setPost(res.data)
    })
},[createPost])

return (
    <div className="home">
        <div style={{display: 'flex',alignItems: 'center'}}>
            <h1>this is the home: Welcome,{userData.username}</h1>
            <Link style={{margin: 10}} to="/home">home</Link>
            <Link style={{margin: 10}} to="/profile">profile</Link>
            <Link style={{margin: 10}} onClick={handletoken} to="/">log out</Link>
        </div>
        <form onSubmit={handleOnSubmit}>
            <input type="text" placeholder="What's happening?" value={createPost} onChange={e => setCreatePost(e.target.value)}/>
            <button type="submit">tweet</button>
        </form>
        <div style={{display: 'flex',flexDirection: 'column'}}>
            {posts.map(post => (
                <div style={{border: '2px solid black',marginBottom: 10,marginRight: 'auto',marginLeft: 'auto',width: 300}} key={post._id}>
                    <div style={{display: 'flex',alignItems: 'center'}}>
                    <Avatar src={post.avatar}/>
                    <span style={{color: 'blue',marginLeft: 10}}>{post.name} <span style={{color: 'grey',fontSize: 11}}>@{post?.username}</span></span><br/>
                    </div>
                    <span>{post.textOfThePost}</span><br/>
                    <span>{moment(post.date).format('lll')}</span>
                </div>
            )).reverse()}
        </div>
    </div>
)
}

解决方法

您只将token存储在localStorage中,因此也没有理由保存上下文。

如果您确实要保存userData,则可以将其保存在localStorage中(或使用其他任何状态管理方式):

function Login() {
  // irrelevant code

  const handleOnSubmit = (e) => {
    e.preventDefault()
    axios.post("http://localhost:5000/login",loginUser)
    .then((res) => {
        const { name,lastname,username,email } = res.data.user;
        const newUserData = { name,email };
        setUserData(newUserData);
        localStorage.setItem('user-data',JSON.stringify(newUserData));
        // irrelevant code
    }); 
  }

  // irrelevant code
}

然后在App的{​​{1}}调用中使用它:

useState
,

此类JS应用程序中的状态在页面刷新之间不会自动保持不变。因此,需要localstorage / sessionstorage / indexdb和类似的机制。您将需要保存状态并在组件加载时对其进行检索。

作为示例,您可以修改home组件以使用如下所示的钩子来保存/检索数据:

function App() {
   // 1. Create a key to name your data in local storage
   const USER_DATA_KEY_IN_LOCALSTORAGE = 'user_data';
   const [userData,setUserData] = useState({
      name: "",lastname: "",username: "",email: "",});

   // 2. Retrieve userData from local storage on startup
   useEffect(() => {
     const userDataString = window.localStorage.getItem(USER_DATA_KEY_IN_LOCALSTORAGE);
     setUserData(JSON.parse(userDataString));
   },[]);

   // 3. Update userData in local storage when it changes.
   useEffect(() => {
     window.localStorage.setItem(USER_DATA_KEY_IN_LOCALSTORAGE,JSON.stringify(userData));
   },[userData]);

   return (
      <Router>
          <userContext.Provider value={{ userData,setUserData }}>
             <div className="app">
             <Switch>
                <Route exact path="/" component={Login} />
                <Route path="/register" component={Register} />
                <Route path="/home" component={Home} />
                <Route path="/profile" component={Profile} />
             </Switch>
             </div>
          </userContext.Provider>
      </Router>
     );
   }

当然,您需要处理userData不存在并且JSON.parse()引发错误和类似内容的情况。