Home > Software design >  React-Router rerenders my component from useEffect, how to mitigate this?
React-Router rerenders my component from useEffect, how to mitigate this?

Time:07-11

I have a useEffect() in my App component, that checks if there is a locally stored user in local storage, and if there is, it updates with dispatch my redux state with the information. This is extremely problematic, because when a person goes to my route player/example, my App component renders the Player component, and then when the user state updates from useEffect, it renders the Player component on the route again. Thus running the component twice, and any useEffects inside my Player component like fetching data, calls data twice!

Is there absolutely anyway to fix this or am I doing my functionality wrong. Its driving me nuts

To sum up, basically the Player component gets rendered twice, because initially the App renders it when there is no user, and when a user is found from the useEffect, it renders it again, even though you stay on the same page with the url player/example

   // React 
    import { useEffect } from 'react'
    
    // Redux
    import { useSelector, useDispatch } from 'react-redux'
    
    // React Reducer
    import { Routes, Route } from "react-router-dom"
    import { loginUser, logoutUser } from './reducers/user';
    
    // Components
    import Login from './components/Login';
    import Player from './components/Player';
    
    function App() {
      console.log("app component")
      const dispatch = useDispatch()
      const user = useSelector(state => state.user)
    
      useEffect(() => {
        const loggedUserJSON = window.localStorage.getItem('loggedUser')
        if (loggedUserJSON) {
          const user= JSON.parse(loggedUserJSON)
          dispatch(loginUser(user))
        }
      }, [])
    
      // User not logged in
      if (!user) { 
        return (
          <Routes>
            <Route path="/login" element={<Login/>}/>
            <Route path="/" element={<Login/>} />
            <Route path="/player/:username" element={<Player/>}/> 
          </Routes>
        )
      }
      
      // User logged in
      return (
        <div>
          <h1><button onClick={() => dispatch(logoutUser())}>LOG OUT</button></h1>
          <Routes>
            <Route path="/login" element={<h1>HOME</h1>}/>
            <Route path="/" element={<h1>HOME</h1>} />
            <Route path="/player/:username" element={<Player/>}/> 
          </Routes>
        </div>
      );
    }
    
    export default App;

CodePudding user response:

It's because there's two return statement in App component. In your case, Player component is not rerendered, it's just switched to other Player component because you returned another element on the user's existence in your App component.

To solve the problem, you have to return one element whether user exists or not.

Here's my code.

function App() {
    console.log("app component")
    const dispatch = useDispatch()
    const user = useSelector(state => state.user)

    useEffect(() => {
        const loggedUserJSON = window.localStorage.getItem('loggedUser')
        if (loggedUserJSON) {
            const user = JSON.parse(loggedUserJSON)
            dispatch(loginUser(user))
        }
    }, [])


    return (
        <div>
            {user && <h1><button onClick={() => dispatch(logoutUser())}>LOG OUT</button></h1>}
            <Routes>
                <Route path="/login" element={<Login />} />
                <Route path="/" element={<Login />} />
                <Route path="/player/:username" element={<Player />} />
            </Routes>
        </div>
    );
}

In this code, Route component is the same component, not a new one and the props hasn't changed, so it won't be rerendered. And you have to implement changing of the view of Login component on the user existence, because this logic belongs to Login component, not App component.

CodePudding user response:

useEffect(() =>
  async function() 
 {
        const loggedUserJSON = await window.localStorage.getItem('loggedUser')
        if (loggedUserJSON) {
          const user= await JSON.parse(loggedUserJSON)
          dispatch(loginUser(user))
        }
      }, [])

You have to use async function inside useEffect

  • Related