Data getting lost after refresh JavaScript-React - javascript

Local storage is getting cleared after refresh tried many StackOverflow solutions but none are solving it. Please help:
Code:
const [data, setData] = useState("");
const [tasks, setTasks] = useState([]);
useEffect(() => {
retrivedTasks();
}, []);
useEffect(() => {
saveToLocal();
}, [tasks]);
const saveToLocal = () => {
localStorage.setItem("tasks", JSON.stringify(tasks));
};
const retrivedTasks = () => {
if (localStorage.getItem("tasks") === null) {
localStorage.setItem("tasks", JSON.stringify([]));
} else {
let dataFromLS = JSON.parse(localStorage.getItem("tasks"));
setTasks(dataFromLS);
}
};

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

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;

React: Update one state variable using another state variable

What I am trying to achieve:
Retrieve book-> take book.chapterIds[0] to update currentChapter -> take currentChapter to update chapters
I am using one state variable(Book) to set another state variable(chapters), like so:
useEffect(() => {
getBook(match.params.id);
// eslint-disable-next-line
}, []);
useEffect(() => {
setCurrentChapter(book.chapterIds[0]);
// eslint-disable-next-line
}, [book]);
useEffect(() => {
getChapter(currentChapter);
// eslint-disable-next-line
}, [currentChapter]);
For second useEffect, I end up getting: Uncaught TypeError: book.chapterIds is undefined
Here is what I tried:
useEffect(() => {
if (Object.keys(book).length !== 0) {
setCurrentChapter(book.chapterIds[0]);
}
// eslint-disable-next-line
}, [book]);
which kinda works, but I still ends up triggering:
useEffect(() => {
getChapter(currentChapter);
// eslint-disable-next-line
}, [currentChapter]);
where both book and currentChapter is undefined
App.js
const [book, setBook] = useState({});
const [chapters, setChapters] = useState({});
const [currentChapter, setCurrentChapter] = useState();
const [loading, setLoading] = useState(false);
const getBook = async (id) => {
setLoading(true);
const res = await axios.get(`<someurl><with id>`);
console.log(res.data);
setBook(res.data.book);
setLoading(false);
};
const getChapter = async (chapterId) => {
if (chapters[chapterId] === undefined) {
console.log(`<someurl><with id & chapterId>`);
setLoading(true);
const res = await axios.get(
`<someurl><with id & chapterId>`
);
setLoading(false);
console.log(res.data);
setChapters({
...chapters,
[chapterId]: res.data.chapter,
});
}
};
Book.js
useEffect(() => {
getBook(match.params.id);
// eslint-disable-next-line
}, []);
useEffect(() => {
if (Object.keys(book).length !== 0) {
setCurrentChapter(book.chapterIds[0]);
}
// eslint-disable-next-line
}, [book]);
useEffect(() => {
getChapter(currentChapter);
// eslint-disable-next-line
}, [currentChapter]);
Also, I get book.chapterIds as undefined on using it inside Book componentreturn()
What am I doing wrong?
Try to set all initial states as null:
const [book, setBook] = useState(null);
const [chapters, setChapters] = useState(null);
const [currentChapter, setCurrentChapter] = useState(null);
Then your useEffects:
useEffect(() => {
getBook(match.params.id);
// eslint-disable-next-line
}, []);
useEffect(() => {
if(book && book.chapterIds?.length > 0)
setCurrentChapter(book.chapterIds[0]);
// eslint-disable-next-line
}, [book]);
useEffect(() => {
if(currentChapter)
getChapter(currentChapter);
// eslint-disable-next-line
}, [currentChapter]);

React making signup checkbox

We are making the necessary consent for membership registration with React. However, it is up to agreeing to the checkbox, but it does not work properly because it is re-rendered when the button is clicked. Any help would be appreciated.
enter code here
const [disabled, setDisabled] = useState('disabled');
const [agree1, setAgree1] = useState(false); //회원정보 동의
const [agree2, setAgree2] = useState(false); //개인정보 수집 및 이용동의
const [agree3, setAgree3] = useState(false); //위치정보 동의
const [total, settotal] = useState(false); //전체 동의
const [email, setemail] = useState("");
const buttonState = useCallback(() => {
if((agree1===true)&&(agree2===true)&&(agree3===true)){
settotal(true);
setDisabled('');
}
else {
setDisabled('disabled');
}
},[agree1,agree2,agree3,total]);
useEffect(() => {
buttonState();
}, [buttonState])
const totalchange = () => {
if(total ===true) { //전체동의가 true라면 다시 클릭 했을때 전부 unchecked
settotal(!total);
setAgree1(!agree1);
setAgree2(!agree2);
setAgree3(!agree3);
} else{ //그외(하나만 체크되 있거나 아무것도 없다면) 전부 checked로 만듬
settotal(true);
setAgree1(true);
setAgree2(true);
setAgree3(true);
setDisabled('');
}
}
const clickFunction = () => {
axios.get(`/signup/authNum?email=${email}`)
.then(res => {
if(window.confirm("인증번호 전송이 완료되었습니다")){
history.push({
pathname : '/registerauth',
state : {state : res.data}
})
}
})
.catch(err => {console.log(err)})
}enter code here
Sorry for being awkward because this is my first question on stack-overflow
Why are you using the callback for the button click and why are you calling that function in useEffect?

React Hook : Correct way of using custom hook to handle onClick Event?

As the title said, what is the correct way of using custom hook to handle onClick Event?
This codesandbox application will display a new quote on the screen when user clicks the search button.
function App() {
const [{ data, isLoading, isError }, doFetch] = useDataApi(
"https://api.quotable.io/random"
);
return (
<Fragment>
<button disabled={isLoading} onClick={doFetch}>
Search
</button>
{isError && <div>Something went wrong ...</div>}
{isLoading ? <div>Loading ...</div> : <div>{data.content}</div>}
</Fragment>
);
}
I created a custom hook called useDataApi() which would fetch a new quote from an API. In order to update the quote when the user clicks the button, inside the useDataApi(), I created a handleClick() which will change the value of a click value to trigger re-render. And this handleClick() function will be return back to App()
const useDataApi = initialUrl => {
const [data, setData] = useState("");
const [click, setClick] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const handleClick = () => {
setClick(!click);
};
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(initialUrl);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [initialUrl, click]);
return [{ data, isLoading, isError }, handleClick];
};
This is working, however, I don't feel this is the correct solution.
I also tried moving the fetchData() out of useEffect and return the fetchData(), and it works too. But according to the React Doc, it says it is recommended to move functions inside the useEffect.
const useDataApi = initialUrl => {
const [data, setData] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(initialUrl);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
useEffect(() => {
fetchData();
}, []);
return [{ data, isLoading, isError }, fetchData];
};
In addition, for creating these kinds of application, is the way that I am using is fine or there is another correct solution such as not using any useEffects or not create any custom Hook?
Thanks
Not sure if this is correct, but here is my solution.
const useDataApi = initialUrl => {
const [data, setData] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const doFetch = async () => {
setIsError(false);
setIsLoading(true);
try {
const result = await axios(initialUrl);
setData(result.data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
return [{ data, isLoading, isError }, doFetch];
};
Btw, don't mutate state directly.
const handleClick = () => {
setClick(!click); // don't do this
setClick(prev => !prev); // use this
};
Your implementation is fine. We are also using something similar. Hope you find it useful.
function useApi(promiseFunction, deps, shouldRun=true){
// promisFunction returns promise
const [loading, setLoading] = useState(false)
const [data, setData] = useState(false)
const [error, setError] = useState(false)
const dependencies: any[] = useMemo(()=>{
return [...dependencyArray, shouldRun]
},[...dependencyArray, shouldRun])
const reload = () => {
async function call() {
try {
setError(null)
setLoading(true)
const res = await promiseFunction();
}
catch (error) {
setError(error)
}
finally {
setLoading(false)
}
}
call();
}
useEffect(() => {
if(!shouldRun) return
setResult(null) //no stale data
reload()
}, dependencies)
return {loading, error, data, reload, setState: setData}
}
Below code will provide some idea about how to use it.
function getUsersList(){
return fetch('/users')
}
function getUserDetail(id){
return fetch(`/user/${id}`)
}
const {loading, error, data } = useApi(getUsersList, [], true)
const {loading: userLoading, error: userError, data: userData}
= useApi(()=>getUserDetail(id), [id], true)

Categories