state value does not change when using useEffect hook - javascript

well I have:
const [bgColor, setBgcolor] = useState(false);
and :
useEffect(() => {
if (window.location.pathname === '/SignUp') {
setBgcolor(true);
} else {
setBgcolor(false);
}
console.log(bgColor)
return () => {
setBgcolor(false);
}
}, [])
What I want to do : is when i reload the page or rerender the page i check the current pathname if it is equal to /Signup I set the bgColor to true but here at every time i reload give me false!

Try this:
const [bgColor, setBgcolor] = useState(false);
const { pathname } = window.location;
useEffect(() => {
if (pathname === '/SignUp') {
setBgcolor(true);
} else {
setBgcolor(false);
}
console.log(bgColor)
return () => {
setBgcolor(false);
}
}, [pathname]);

Related

Cant use localStorage in nextJS

I'm developing a cart system and the problem is that, when I add a product to the cart, it works in context and localStorage; but, when I refresh, the data is gone.
const dispatch = useDispatch();
const {
cartItems
} = useSelector((state) => state.cart)
const [cartState, setCartState] = useState({
cartItems: [],
})
const initialRender = useRef(true);
useEffect(() => {
if (JSON.parse(localStorage.getItem("cartState"))) {
const storedCartItems = JSON.parse(localStorage.getItem("cartState"));
setCartState([...cartItems, ...storedCartItems]);
}
}, []);
useEffect(() => {
if (initialRender.current) {
initialRender.current = false;
return;
}
window.localStorage.setItem("cartState", JSON.stringify(cartState));
}, [cartState]);
What I usually do is have some state to check against to see if the client side is loaded:
const [clientLoaded, setClientLoaded] = useState(false);
useEffect(() => {
setClientLoaded(true);
}, []);
Then anywhere you can check if clientLoaded is true, for example in another useEffect hook:
useEffect(() => {
clientLoaded && doWhatever; // do whatever you want here
}, [clientLoaded]);
Or in you render method:
{clientLoaded && <span>Render this if the client is loaded</span>}
You are earsing the value of the local storage on the first render
useEffect(() => {
if (initialRender.current) {
initialRender.current = false;
return;
}
//here
window.localStorage.setItem("cartState", JSON.stringify(cartState));
}, [cartState]);
You should :
useEffect(() => {
if (initialRender.current) {
initialRender.current = false;
} else {
window.localStorage.setItem("cartState", JSON.stringify(cartState));
}
}, [cartState]);

Cannot read property of null when extracting data from state

I'm trying to display a navigation item when a flag is true, but the problem is, when I try to get the following data from it, it returned me undefined, I created the following for that:
let navigate = useNavigate();
const userSignin = useSelector((state: RootStateOrAny) => state.userSignin);
const { userInfo } = userSignin;
const checkAdmin = useCallback(() => {
if (userInfo) {
if (typeof userInfo.user === "undefined") {
return null;
} else {
return userInfo.user.isAdmin;
}
} else {
return null;
}
}, []);
useEffect(() => {
checkAdmin();
if (!userInfo.user.isAdmin) {
navigate("/");
}
}, [checkAdmin]);
I did the checkAdmin function, because before that I had userInfo.user.isAdmin and it returned me undefined.
{checkAdmin() && (
<NavbarItem
component='li'
onMouseEnter={() => setTopMenuIndex(4)}
onMouseLeave={() => setTopMenuIndex(-1)}
>
<Box
style={{ whiteSpace: "nowrap" }}
component='a'
{...{ href: "/createItem" }}
>
{topMenuIndex === 4 && <Tippy topMenuIndex={topMenuIndex} />}
Admin Dashboard
</Box>
</NavbarItem>
)}
Now I want to make sure that if you don't have that flag, you will get redirected to the homepage, but using the userInfo.user.isAdmin is returning null now. How can I recode this logic to be better or how can I make at least this useEffect work correctly.
Firstly you are passing checkAdmin in useEffect inside an array, but it is a function. According to my knowledge you can only pass state or props to refresh the component or re-render.
I am not sure what exactly the ask was but, according to me.
let navigate = useNavigate();
const userSignin = useSelector((state: RootStateOrAny) => state.userSignin);
const { userInfo } = userSignin;
// Old Node Version
const checkAdmin = () => {
if(userInfo) {
if(userInfo.user) {
return userInfo.user.isAdmin
}
};
return false;
};
// New Node Version
const checkAdmin = () => {
if(userInfo?.user?.isAdmin) {
return userInfo.user.isAdmin
};
return false;
};
useEffect(() => {
if (!checkAdmin()) {
navigate("/");
}
}, [userInfo]);

Callback in Custom React Hooks

I have the following hooks:
function useLogin(state, url, loginMessage, callback) {
const history = useHistory();
const logged_in = state.user.authenticated;
useEffect(() => {
if (!logged_in) {history.push(url); loginMessage();}
else callback();
}, [logged_in])
return logged_in;
}
function useGroupAuth(state, url, loginMessage) {
const history = useHistory();
let has_group_auth = false;
state.user.available_teams.forEach(function(currentValue) {
if (currentValue.toString().toLowerCase() === teamname.toString().toLowerCase()) {
has_group_auth = true;
}
})
useEffect(() => {
if (!has_group_auth) {
if (state.user.available_teams.length != 0) {
history.push(url); loginMessage();
}
else
history.push("/"); loginMessage();
} else {
callback();
}
}, [has_group_auth])
return has_group_auth;
}
and they're used as
let loggedin = useLogin(state, "/accounts/login", teamhome2_message);
let properauth = useGroupAuth(state, ("/team/" + state.user.available_teams[0]), teamhome3_message);
useEffect(() => {
if (loggedin)
if (properauth)
checkteamexists(teamname);
}, []);
The problem is that, even though the code compiles, it's not behaving as I wanted it to. I only want if (properauth) to execute if loggedin is true.
My previous implementation worked because I was simply using callback without any custom hooks, as such:
useEffect(() => {
checklogin(function() {
checkauth(function() {
checkteamexists(teamname);
})
})
}, []);
How can I ensure that properauth won't execute unless loggedin is true, as described in the initial, hook-less useEffect hook?
Thanks in advance.
In your case, you can't update the useGroupAuth value. because it's returning only one value send one more variable(callback) to update/check whenever you need it. something like useState
Hook
function useGroupAuth(state, url, loginMessage) {
const history = useHistory();
const [has_group_auth, setAuth] = useState(false);
const validate = () => {
setAuth(
state.user.available_teams.some(
(currentValue) =>
currentValue.toString().toLowerCase() ===
teamname.toString().toLowerCase()
)
);
};
useEffect(validate, []);
useEffect(() => {
if (!has_group_auth) {
if (state.user.available_teams.length != 0) {
history.push(url);
loginMessage();
} else history.push("/");
loginMessage();
} else {
callback();
}
}, [has_group_auth]);
return [has_group_auth, validate];
}
Use
let [properauth, reValidate] = useGroupAuth(state, ("/team/" + state.user.available_teams[0]), teamhome3_message);
useEffect(() => {
if (loggedin){
// Do something
reValidate();
}
}, []);
It seems you are missing dependencies in your useEffect hook. Both loggedin and properauth (teamname as well, really) are referenced in the effect callback, so they should be included in the effect's dependencies.
const loggedin = useLogin(state, "/accounts/login", teamhome2_message);
const properauth = useGroupAuth(state, ("/team/" + state.user.available_teams[0]), teamhome3_message);
useEffect(() => {
if (loggedin && properauth && teamname) {
checkteamexists(teamname);
}
}, [loggedin, properauth, teamname]);

How to call a function after setting state is complete in useEffect?

I would like to run customFunction only when customEffect has finished setting isReady state. And customFunction should only run once no matter if the isReady was set to false or true as long as it was ran after it was set.
import customFunction from 'myFile';
export const smallComponent = () => {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const customEffect = async () => {
try {
const response = await get(
`some-api.com`,
);
return setIsReady(response); // response can be true or false
} catch {
return null;
}
};
customEffect();
customFunction();
}, []);
return (
<>Hello World</>
)
}
I tried to add isReady as second useEffect argument, but then my customFunction is being run before the customEffect finishes and then again after the isReady is being set.
Also tried having in a separate useEffect, but still seems to run before the customEffect finishes.
Set initial value to null and use separate useEffect as Kevin suggested (only without checking isReady true/false).
In this case setIsReady will change isReady from null to true/false and the second useEffect will be called.
import customFunction from 'myFile';
export const smallComponent = () => {
const [isReady, setIsReady] = useState(null);
useEffect(() => {
const customEffect = async () => {
try {
const response = await get(
`some-api.com`,
);
return setIsReady(response);
} catch {
return null;
}
};
customEffect();
}, []);
useEffect(() => {
if (null === isReady) {
return;
}
customFunction();
}, [isReady]);
return (
<>Hello World</>
)
}
Since you want to cue an effect to run after the isReady state is set, and the value of isReady is irrelevant you can to use a second state value to indicate the first effect and state update has completed.
This will trigger the second effect to invoke customFunction but you don't want your component to remain in this state as from here any time the component rerenders the conditions will still be met. You'll want a third "state" to indicate the second effect has been triggered. Here you can use a React ref to indicate this.
export const smallComponent = () => {
const [readySet, setReadySet] = useState(false);
const [isReady, setIsReady] = useState(false);
const customFunctionRunRef = useRef(false);
useEffect(() => {
const customEffect = async () => {
try {
const response = await get(
`some-api.com`,
);
setReadySet(true); // to trigger second effect callback
return setIsReady(response); // response can be true or false
} catch {
return null;
}
};
customEffect();
}, []);
useEffect(() => {
if (readySet && !customFunctionRunRef.current) {
// won't run before readySet is true
// won't run after customFunctionRunRef true
customFunction();
customFunctionRunRef.current = true;
}
}, [readySet]);
return (
<>Hello World</>
);
}
Better solution borrowed from #p1uton. Use null isReady state to indicate customFunction shouldn't invoke yet, and the ref to keep it from being invoked after.
export const smallComponent = () => {
const [isReady, setIsReady] = useState(null);
const customFunctionRunRef = useRef(false);
useEffect(() => {
const customEffect = async () => {
try {
const response = await get(
`some-api.com`,
);
return setIsReady(response); // response can be true or false
} catch {
return null;
}
};
customEffect();
}, []);
useEffect(() => {
if (isReady !== null && !customFunctionRunRef.current) {
// won't run before isReady is non-null
// won't run after customFunctionRunRef true
customFunction();
customFunctionRunRef.current = true;
}
}, [isReady]);
return (
<>Hello World</>
);
}
I'm not sure if I understood you correctly, but this is how I would use a separate useEffect.
import customFunction from 'myFile';
export const smallComponent = () => {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const customEffect = async () => {
try {
const response = await get(
`some-api.com`,
);
return setIsReady(response);
} catch {
return null;
}
};
customEffect();
}, []);
useEffect(() => {
if (!isReady) {
return;
}
customFunction();
}, [isReady]);
return (
<>Hello World</>
)
}
Have you tried using this package, isMounted?
I used that in my projects.
import React, { useState, useEffect } from 'react';
import useIsMounted from 'ismounted';
import myService from './myService';
import Loading from './Loading';
import ResultsView from './ResultsView';
const MySecureComponent = () => {
const isMounted = useIsMounted();
const [results, setResults] = useState(null);
useEffect(() => {
myService.getResults().then(val => {
if (isMounted.current) {
setResults(val);
}
});
}, [myService.getResults]);
return results ? <ResultsView results={results} /> : <Loading />;
};
export default MySecureComponent;
https://www.npmjs.com/package/ismounted

Circular dependency in useEffect

I'm trying to build a custom hook that has to alter values before using Formik's setFieldValue().
Here is how to hook looks like:
export const useCount = (tab) => {
const { values, setFieldValue } = useFormikContext();
const { count} = values;
useEffect(() => {
const handleChange = () => {
if (tab === 0) {
setFieldValue("count", count);
} else if (tab === 1) {
setFieldValue("count", count * 2);
}
};
handleChange();
}, [count]);
};
The issue at hand is that I end up with an infinite loop, due to the fact that I'm changing count inside the useEffect(). Any clues?
Replace count on tab in your useEffect, to trigger this effect only when tab is changed
export const useCount = tab => {
const {values: {count}, setFieldValue} = useFormikContext();
const handleChange = () => {
if (tab === 0) {
setFieldValue("count", count);
} else if (tab === 1) {
setFieldValue("count", count * 2);
}
}
useEffect(() => {
handleChange();
}, [tab]);
};

Categories