I am working on a fullstack project with ReactJS in front and it has user management.
Some pages are available only if the user is connected, so basically, I am checking it by asking my server, and I am doing it in my NavBar.js
component (because it used in every pages).
it looks like something like this:
const Navigation = ({ callback }) => {
useEffect(() => {
axios({
method: "GET",
withCredentials: true,
url: process.env.REACT_APP_SERVER "/user",
}).then((res) => {
if (callback)
callback(res.data.username);
setUser(res.data.username);
}).catch((e) => {
if (callback)
callback(null);
});
}, [callback])
return (
<div>Some navbar stuff</div>
)
}
export default Navigation;
and inside my other pages, I use my Navigation component like this :
const PublishContent = () => {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState(null);
const handleUserName = e => {
setUser(e);
setLoading(false);
}
return (
<div>
<Navigation callback={handleUserName} />
{
!loading && <div>{user ? <div>CONNECTED</div> : <div> NOT CONNECTED</div>} </div>
}
</div>
)
}
export default PublishContent;
My problem is that my handleUserName
is called three times, I don't understand why, even if callback
is the the dependency list of my UseEffect
, it should be constant...
Also, since useState
is async, even if the user is logged, the first time my user
is undefined.
Do you know how I can solve this problem ? Also, can you tell me if the way I am doing (checking if user is authenticated, sounds weird to you ? Thank you.
CodePudding user response:
Every time handleUserName
is called it's going to call setUser and setLoading which both will initiated a re-render. When the PublishContent component re-renders (twice in this instance) it will create a new function and assign it to handleUserName which then gets passed into your Navigation component and into your useEffect, causing your useEffect to re-run since the dependency has changed (new function as a result of re-rendering). To solve your problem you just need to wrap handleUserName in a useCallback like so
const handleUserName = useCallback((e) => {
setUser(e)
setLoading(false)
}, [])
You also shouldn't be calling setUser in Navigation. If you need to access it you can pass it down as a prop
const PublishContent = () => {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState(null);
const handleUserName = useCallback(e => {
setUser(e);
setLoading(false);
}, [])
return (
<div>
<Navigation callback={handleUserName} user={user} />
{
!loading && <div>{user ? <div>CONNECTED</div> : <div> NOT CONNECTED</div>} </div>
}
</div>
)
}
export default PublishContent;