-1

I am creating a social media app and when I log in with the user, I'm saving the token in the local storage and also putting in context the user's information to use it in several components after, and its working, but when I refresh the pages, the data disappear and the context is undefined. What am I doing wrong?

My 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>
     );
   }

My 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>
)
}

My Home.js where if I refresh I lose the data from the context

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>
)
}
skyboyer
  • 22,209
  • 7
  • 57
  • 64

2 Answers2

0

You only store the token in the localStorage, so there is no reason the context would be saved as well.

If you do want to save userData, you can save it in the localStorage (or use any other way of state management):

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, lastname, username, email };
        setUserData(newUserData);
        localStorage.setItem('user-data', JSON.stringify(newUserData));
        // irrelevant code
    }); 
  }

  // irrelevant code
}

And then use it inside App's useState call:

function App() {
   const persistentUserData = localStorage.getItem('user-data');
   const [userData, setUserData] = useState(JSON.parse(persistentUserData));

   // return
}
GalAbra
  • 5,048
  • 4
  • 23
  • 42
  • 1
    thank you so much! I was putting before the user data in the local storage too, it seems that i was missing what you did in app.js. Now its working – gonzalovc15 Nov 03 '20 at 19:04
0

State in such JS apps does not automatically persist between page refreshes. Hence the need for localstorage / sessionstorage / indexdb and similar mechanisms. You will need to save the state and retrieve it on component load.

As an example, you could modify your home component to save / retrieve data using a hook like this:

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>
     );
   }

Of course, you'll need to handle cases where userData doesn't exist and JSON.parse() throws an error and similar stuff.