Does session storage have a delay while fetching? - javascript

In my react code, inside a component, I am fetching a value from session storage(inside useEffect hook). When console printed, it shows the value.
But inside the render(or return method), it does not have the value just fetched. Is there a delay while fetching from session storage?
Circumvented the problem after storing the same in state and fetching inside render!
let myValue = '';
useEffect(()=>{
myValue = sessionStorage.getItem("someKey");
},[]);
// In the return method
return {
<div>{myValue}</div>
}
Why does value fetched from session storage not available immediately in render?

The issue here is that you're expecting a variable value change to trigger a re-render. React doesn't work this way and you'll need another approach if you want to change a value and have it re-render:
Consider:
const [myValue, setMyValue] = useState('');
useEffect(()=>{
setMyValue(sessionStorage.getItem("someKey"));
},[]);
// In the return method
return {
<div>{myValue}</div>
}

No, both localStorage and sessionStorage calls are sync
You don't see the value in the render because the view is not re-rendered. You have to set a state, get a new props or force the render to see it.
How to force update in hooks
const [, updateState] = React.useState();
const forceUpdate = useCallback(() => updateState({}), []);

It's available immediately
return {
<div>{sessionStorage.getItem("someKey")}</div>
}

No, all sessionStorage calls are synchronous.

Related

React Native, pushing items to an array state not working

I need to push items to my array, but when I console.log this array, it says 'undefined' for the specific value. I'm trying btw to fetch data from firebase storage. How do I correctly add items to an Array
Here is my code:
const [imagelinks, setImagelinks] = React.useState(['']);
const myFunction = () =>{
await storage()
.ref(`${_userid}`)
.list()
.then(result => {
result.items.forEach(async ref => {
await storage()
.ref(ref.fullPath)
.getDownloadURL()
.then(url => {
//get url
setImagelinks([...imagelinks, url]);
console.log('Links: ' + url);
});
});
//there it says undefined when logging...
console.log(imagelinks[0])
});
}
Edit: can I use following?
const imagelinks = [];
//instead of
const [imagelinks, setImagelinks] = useState([]);
Updating state is an asynchronous task. A re-render of the component is required in order to have the updated value. To be sure, add your console outside of myFunction, below the state definition for example:
const [imagelinks, setImagelinks] = React.useState(['']);
console.log(imagelinks)
If you wanna use the result to do some logic (an API call for example), you can use the useEffect hook, like this for example:
useEffect(()=>{
if(imagelinks.length>0){
// do your things, except do not call setImagelinks in here to avoid having a loop because imagelinks is in the dependencies array of useEffect.
}
},[imagelinks])
As the previous answer mentioned, you can't access the new state in the same scope after setting it as it will still have the old value for that specific scope.
If you really need to store and access some value in the same scope you can use useRef hook or you can store the value in a variable in that scope.
Here is an example, but keep in mind that changing the referenced value will not trigger a re-render so this will not replace your useState.
variable example:
let imageLinksArray=[...imagelinks, url] //add this in your function
console.log(imageLinksArray) //you can pass this to the next function
useRef example:
const imagelinksRef = useRef() //add this on top after your useState
...
imageLinksRef.current=[...imagelinks, url] //add this in your function
console.log(imageLinksRef.current) //this will give you the result you're expecting
you can also check out this npm package that let's you access the new state through a ref directly after setting it (if you really need to)
https://www.npmjs.com/package/react-usestateref

How do i re-run useEffect hook if the localstorage value has been changed?

I'm trying to re-run the useEffect hook based if any new item has been added to the localstorage.
My useEffect looks like this :
useEffect(() => {
//getting all localstorage items and setting to a variable
const localStorageItems = { ...localStorage }
// filtering out the localstorage items for keys that only starts with "HTTP" & pushing it to the state called "testData" using setTestData useState hook.
const filteredByKey = Object.fromEntries(
Object.entries(localStorageItems).filter(([key, value]) => {
if (key.startsWith("http")) {
testArr2.push({ urls: [key, value] })
// setTestData({ urls: [key, value] })
setTestData((prev) => [...prev, { key, value }])
}
})
)
}, [])
My problem is when I hit a button(adding a new data) this useEffect should ideally re-run, setting the latest data to the "testData" state.
The Problem :
I cannot pass window.localstorage to the useEffect's array dependency list, as it is external variable I believe and react doesn't allow it.
What I have tried:
I have looked at this answer on SO, but my situation is a bit complex as I'm loading the entire items to a variable first(I'm not aware of any better alternatives)
I have tried to put the state value in the dependency list, but it doesn't work and goes to infinite loop.
Please help.
Thanks for reading this far.
i can give you a work around for this use case, create react context that syncs with localStorage. you can use the context as a useEffect dependency. sync means, take the value from localStorage at starting and update the context along with the localStorage from your app.
Define a state that holds the local storage value and pass it as a useEffect parameter.
const [localStorageData, setLocalStorageData] = useState([]);
useEffect(() => {
// Your codes
}, [localStorageData])
you can use 'storage' event listener
window.addEventListener('storage', function(e) {
if (e.newValue) {
console.log('new data is saved ...')
// ...... your code
}
})

React useState hook is not working when submitting form

I am new to React and I have some doubt regarding useState hook.I was recently working on an API based recipe react app .The problem I am facing is when I submit something in search form a state change should happen but the state is not changing but if I resubmit the form the state changes.
import React,{useState,useEffect} from "react";
import Form from "./componnents/form";
import RecipeBlock from "./componnents/recipeblock"
import './App.css';
function App() {
const API_id=process.env.REACT_APP_MY_API_ID;
const API_key=process.env.REACT_APP_MY_API_KEY;
const [query,setQuery]=useState("chicken");
const path=`https://api.edamam.com/search?q=${query}&app_id=${API_id}&app_key=${API_key}`
const [recipe,setRecipe]=useState([]);
useEffect(() => {
console.log("use effect is running")
getRecipe(query);
}, []);
function search(queryString){
setQuery(queryString);
getRecipe();
}
async function getRecipe(){
const response=await fetch(path);
const data=await response.json();
setRecipe(data.hits);
console.log(data.hits);
}
queryString in search() function holds the value of form input,Every time I submit the form this value is coming correctly but setQuery(queryString) is not changing the query value or state and if I resubmit the form then it change the state.
The code you provided something doesn't make sense.
useEffect(() => {
console.log("use effect is running")
getRecipe(query);
}, []);
Your getRecipe doesn't take a variable. But from what I am understanding whenever you search you want to set the Query then get the recipe from that Query.
With the useEffect you can pass in a parameters to check if they changed before running a function. So update the setQuery then when the component reloads it will fire the useEffect if query has changed. Here is the code to explain:
useEffect(() => {
console.log("use effect is running")
getRecipe(query); <-- this doesn't make sense on your code
}, [query]);
function search(queryString){
setQuery(queryString);
}
By doing this when the state updates it causes the component to re-render and therefore if query has changed it will call your getRecipe function.
The main issue in your code is that you are running getRecipe() directly after setQuery(queryString). setQuery(queryString) is asynchronous and will queue a state change. When you then run getRecipe() directly after, the state will still hold the old value of query (and path) and therefore does not fetch the new data correctly.
One solution would be to call getRecipe() within a useEffect() dependent on path.
useEffect(() => {
getRecipe();
}, [path]);
function search(queryString){
setQuery(queryString);
// getRecipe() <- removed
}
With [path] given as dependencies for useEffect(), getRecipe() will be called automatically whenever path changes. So we don't have to call it manually from search() and therefore can remove getRecipe() from the function body. This also makes the current useEffect() (without [path] dependency) redundant, so it can be removed.
Another solution would be to provide the new query value through the getRecipe() parameters, removing the dependency upon the state.
function search(queryString){
setQuery(queryString);
getRecipe(queryString);
}
async function getRecipe(query) {
const path = `https://api.edamam.com/search?q=${query}&app_id=${API_id}&app_key=${API_key}`;
const response = await fetch(path); // <- is no longer dependent upon the state
const data = await response.json();
setRecipe(data.hits);
}
This does require moving the path definition inside getRecipe().

Do functions get the latest state value in React?

I have a function inside of my functional component that uses a value saved in state. However, when it is called, it has the original value in state, not the updated value. When I look at my component in Chrome React Dev Tools, I see that the updated value is stored in state. Aren't functions supposed to get the latest state value in React? I didn't think I'd have to wrap my functions in a useEffect every time some value in state they depend on changes. Why is this happening?
const Editor = (props) => {
const [template, setTemplate] = useState(null);
const [openDialog, setOpenDialog] = useState(false);
useEffect(() => {
if (props.templateId) {
getTemplate(props.templateId));
}
},[]);
const getTemplate = (templateId) => {
{...make API to get template...}
.then((response) => {
if (response.template) setTemplate(response.template);
});
}
/* THIS FUNCTION SAYS TEMPLATE IS ALWAYS NULL */
const sendClick = async () => {
if (template) {
await updateTemplate();
} else {
await initializeTemplate();
}
setOpenDialog(true);
};
}
UPDATE: I figured out the issue. The sendClick function is being used inside an object that I have in state. When that object is created, it creates a version of the sendClick function based on the state at that time. I realized I needed to refactor my code so that the function is not stored within my object in state so that the function will always have the latest state values.
Please correct the code there its setTemplate(template)); not getTemplate(template));
I'm guessing that you have that right in the source code... if Yes then,
You have got into a trap that all developers new to React fall into.
This code is betraying you ...
useEffect(() => {
if (props.template) {
setTemplate(template)); // Mentioned as getTemplate(template));
}
},[]); // Here is where you make the mistake
The second argument you pass to the useEffect is called as Dependencies. Meaning if your useEffect is dependent on any state or any variable or function, Ii should be pass as the second argument inside the []. By now you should have got the answer.
Clearly, your useEffect is dependent on template. You should pass that inside the [].
So the code will be : -
useEffect(() => {
if (props.template) {
setTemplate(template)); // Mentioned as getTemplate(template));
}
},[template]);
Now React will automatically run the function every time the value of template changes therefore, updates template.
For more information about useEffect ...
Refer React Documentation
Refer the useEffect API

How to avoid setState in useEffect hook causing second render

I'm writing a custom hook that takes an id as input and then should do the following tasks:
get data for that id synchronously from a store
subscribe to changes in this store and update data accordingly
I came up with the following implementation which has the downside that I am setting state directly from inside the effect hook, causing a second render.
function useData({id}) {
// set initial data on first render
const [data, setData] = useState(Store.getData(id));
useEffect(() => {
// when id changes, set data to whatever is in store at the moment
// here we call setData directly from inside useEffect, causing a second render
setData(Store.getData(id));
// subscribe to changes to update data whenever it changes in the store
return Store.onChange(id, setData);
}, [id]);
return data;
}
A second approach I tried was to add a dummy state that is only there to cause a re-render. In this approach I am directly returning the data received from Store.getData(). This ensures that I will get the freshest data with every render and the useEffect ensures that every onChange trigger will cause a new render.
function useData({id}) {
// adding some dummy state that we only use to force a render
const [, setDummy] = useState({});
const refresh = useCallback(() => {
setDummy({});
}, []);
useEffect(() => {
// subscribe to changes cause a refresh on every change
return Store.onChange(id, refresh);
}, [id, refresh]);
return Store.getData[id];
}
The second approach works well but it feels weird to add this dummy state. Sure, I could put this into another useRefresh hook but I am not sure if this would really be a good practice.
Is there any better way of implementing this, without calling setData directly from inside useEffect and without relying on some unused dummy state?
So by now you use useState inside your hook just to re-trigger rendering of host component once store is changed. How about taking change handler from the outside?
function useData(id, onChanged) {
useEffect(() => {
return store.onChange(id, onChanged);
}, [id, onChanged]);
return store.getData(id);
}

Categories

Resources