Cant use localStorage in nextJS - javascript

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

Related

Way to invoke function again while not setting different value in state

So I have built app which takes value from input -> set it to the state-> state change triggers functions in useEffect (this part is in custom hook) -> functions fetch data from api -> which triggers functions in useEffect in component to store data in array. The thing is that there are two problems that I am trying to solve :
When user is putting the same value in input and setting it in state it's not triggering useEffect functions (I solved it by wrapping value in object but I am looking for better solution).
When user uses the same value in short period of time api will send the same data which again makes problem with triggering function with useEffect (I tried to solved with refresh state that you will see in code below, but it looks awful)
The question is how can I actually do it properly? Or maybe the solutions I found aren't as bad as I think they are. Thanks for your help.
component
const [nextLink, setNextLink] = useState({ value: "" });
const isMounted = useRef(false);
const inputRef = useRef(null);
const { shortLink, loading, error, refresh } = useFetchLink(nextLink);
const handleClick = () => {
setNextLink({ value: inputRef.current.value });
};
useEffect(() => {
setLinkArr((prev) => [
...prev,
{
id: prev.length === 0 ? 1 : prev[prev.length - 1].id + 1,
long: nextLink.value,
short: shortLink,
},
]);
if (isMounted.current) {
scrollToLink();
} else {
isMounted.current = true;
}
inputRef.current.value = "";
}, [refresh]);
custom hook
const useFetchLink = (linkToShorten) => {
const [shortLink, setShortLink] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [refresh, setRefresh] = useState(false);
const isMounted = useRef(false);
const fetchLink = async (link) => {
setLoading(true);
try {
const response = await fetch(
`https://api.shrtco.de/v2/shorten?url=${link}`
);
if (response.ok) {
const data = await response.json();
setShortLink(data.result.short_link);
setRefresh((prev) => !prev);
} else {
throw response.status;
}
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (isMounted.current) {
if (checkLink(linkToShorten.value)) {
setError(checkLink(linkToShorten.value));
} else {
fetchLink(linkToShorten.value);
}
} else {
isMounted.current = true;
}
}, [linkToShorten]);
const value = { shortLink, loading, error, refresh };
return value;
};
export default useFetchLink;

state value does not change when using useEffect hook

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

React Native How Do I keep the user Loged in when he close the app

I am trying to save the user data when he loged in like this.
const handleLogin = () => {
firebase
.auth()
.signInWithEmailAndPassword(Email, passWord)
.then((res) => {
firebase.auth().onAuthStateChanged((userData) => {
setuserData(userData);
const jsonValue = JSON.stringify(userData);
AsyncStorage.setItem("userData", jsonValue);
console.log(userData);
});
})
.then(() => navigation.navigate("HomeScreen"))
.catch((error) => console.log(error));
};
and in the Spalch I am trying to check if the userData is in local storage or not .the problem is that it goes directly to HomeScreen even if there is No Data in Local storage
any help please
const SplashScreen = ({ navigation }) => {
const [animating, setAnimating] = useState(true);
useEffect(() => {
setTimeout(() => {
setAnimating(true);
navigation.replace(AsyncStorage.getItem("userData") ? "HomeScreen" : "Log_In");
}, 500);
},
[]);
AsyncStorage.getItem returns promise so either you need to write it with async/await or in promise
useEffect(() => {
setTimeout(async() => {
setAnimating(true);
const user = await AsyncStorage.getItem("userData")
navigation.replace(user ? "HomeScreen" : "Log_In");
}, 500);
},
[]);
Here is my solution for this, a bit long but you can try it out
const SplashScreen = ({ navigation }) => {
const [animating, setAnimating] = useState();
const [redirect, setRedirect] = useState('');
const getUserData = useCallback(async () => {
const response = await AsyncStorage.getItem('userData');
setRedirect(response ? 'HomeScreen' : 'Log_In');
},[]);
useEffect(() => {
setTimeout(() => {
getUserData();
}, 500);
}, []);
useEffect(() => {
if (redirect) {
setAnimating(true);
navigation.replace(redirect);
}
}, [redirect]);
};

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

Why does useState react hook cause Too many re-renders. React limits the number of renders to prevent an infinite loop

I have the following script:
const [customers, setCustomer] = useState([]);
if(users.results){
users.results.filter(user => {
user.roles.filter(role => {
if(role.role.name === 'ADMIN'){
admins.push(user);
}
});
});
let x = []
users.results.filter(user => {
user.roles.filter(role => {
if (role.role.name === 'CUSTOMER') {
x.push(user);
}
});
});
setCustomer(x);
}
Trying to call setCustomer causes the Too many re-renders. React limits the number of renders to prevent an infinite loop. error. I can't seem to find the reason why.
How do I set the customers to the value of x without causing the above error?
UPDATED CODE
const Administration = props =>{
const { fetchUsers, users, loading_users } = props;
const usersPerPage = 9
useEffect(() => {
fetchUsers();
}, []);
let admins = [];
const [customers, setCustomer] = useState([]);
if(users.results){
users.results.filter(user => {
user.roles.filter(role => {
if(role.role.name === 'ADMIN'){
admins.push(user);
}
});
});
let x = []
users.results.filter(user => {
user.roles.filter(role => {
if (role.role.name === 'CUSTOMER') {
x.push(user);
}
});
});
setCustomer(x);
}
It sounds like you only want to update customer with setCustomer when users updates. Placing this in a useEffect with users as a "only call this when this changes" option can do that for you. It also looks like admins is supposed to be state:
const [admins, setAdmins] = useState([]);
useEffect(() => {
if(users.results){
let admins = [];
users.results.filter(user => {
user.roles.filter(role => {
if(role.role.name === 'ADMIN'){
admins.push(user);
}
});
});
setAdmins(admins);
let x = []
users.results.filter(user => {
user.roles.filter(role => {
if (role.role.name === 'CUSTOMER') {
x.push(user);
}
});
});
setCustomer(x);
}
}, [users]);
Now this will only run on mount and when the users state changes.

Categories