Is it ok to update state very second? - reactjs

I am trying to display the count of items in a cart. I want the count to update every second so if a user adds or deletes from cart it will make that request every second and refresh, updating the state and the number will change. I just tried this method and it works fine, but I'd like to know if it's ok to do or if there is a better way of doing it.
const [update, setUpdate] = useState(0)
const [data, setData] = useState([])
let currentUser = 1
const getData = () => {
axios.get(`http://localhost:4000/api/userCart/${currentUser}`)
.then((res) => {
setData(res.data)
setUpdate(++update)
})
}
useEffect(() => {
getData()
}, [update])
useEffect(() => {
setInterval(() => {
getData()
}, 1000);
},[])

I think thats ok, you need just a way to clear this interval when you destroy the component
const timer = useRef<any>(null);
timer.current = setInterval(() => {
//your interval code
}, time);
useEffect(()=>{
return () => {
clearInterval(timer.current);
}
},[])
your first useEffect I think can be a problem, you made a interval and a effect that runs every get

It's okay when you want to give a real-time like experience. If this will be on production you need to consider how many request will be done and the time it can take to resolve and get the data.
There's a pacakge SWR from Vercel team which you can use https://swr.vercel.app/docs/revalidation , it fetches data, validates it's state and serves a cached state when available. Give it a try
If you want to continue with your own implementation then you need to take into consideration this:
Intervals will keep fetching data don't caring if previous fetch was completed. Solution: Fetch data then run a setTimeout and resolve with a new fetch
Clean up. Save each timeout in a Ref and when a component unmounts clear that timeOut
There's no correct way of doing stuff, give any idea you have a try and if it works the just polish it and avoid any side effects as the mentioned above : )
To consider in your current code
In the code you shared, the getData function is being invoked twice, one from interval which then keeps requestin data, and again when you update the update prop.
A refactor idea can be this:
// Out of component body
const UPDATE_INTERVAL = 1000
// In component body
const [update, setUpdate] = useState(0)
const [data, setData] = useState([])
const timer = useRef(null)
useEffect(() => {
const triggerUpdate = setUpdate((n) => n + 1)
const getData = () => {
return axios.get(`http://localhost:4000/api/userCart/${currentUser}`)
}
getData()
.then((res) => {
setData(res.data)
timer.current = setTimeout(triggerUpdate, UPDATE_INTERVAL)
})
.catch(console.error)
return () => {
clearTimeout(timer.current)
}
}, [update])

Related

useEffect is causing infinite loop when use state as dependency

Here simply I am fetching data from mysql DB and storing it in state and in order to fetch this data:
const [orders, setOrders] = useState([]);
To fetch data I am using different functions and finally I am calling those functions using useEffect simple enough and so for everything is working perfectly but the problem comes whenever I use the state as dependency where I am storing data beacause if I dont do that then I have to manually refresh the page for latest changes and I have tried every given solution on stackoverflow but any of the solution didnt work so someone can please help me how can I use this state as dependencey without causing infinite loop:
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
loadData();
loadTotal();
}, [orders]);
console.log(orders);
If you move the state into the useEffect dependency, you can then check if it is empty, and only set it when that check passes.
It will set the state once to populate and not pass the check again.
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
if(orders.length === 0) {
loadData();
}
// you can do the same with checking loadTotal() state
}, [orders]);
console.log(orders);
Avoid ,non-primitive data types in dependencyArray ,
useEffect(() => {
loadTotal();
loadData();
}, [total, orders.length]);
every times you "setOrders" means you change the state,every times you change the state,means the "useEffect" will do again.that cause infinite loops.why not try useEffect(() => {loadData()}, [])?

Infinite Loop React

I'm new in React, and i have problems to show the content of an array.
I have a state to store the data (initial value []). Then, I have a function to consume frmo an api with a get, then i store that data in the state, and finally, I iterate over the state to show it on the page. Everything's is ok, the function gives return the values, but in the console, the return is enter in a infinite loop, slowing down the browser.
Here I detach the code
The state where i store the values
const [projects, setProjects] = useState([]);
Function that consumes the api and save it in the state
const getData = async () => {
let allProjects = {};
const res = await axios.get(URL_BASE);
allProjects = res.data;
setProjects(allProjects);
console.log(projects);
};
useEffect to refresh the render when projects is change
useEffect(() => {
getData();
}, [projects]);
This is where i iterate
return (
<div>
<p>La cantidadd de proyectos en total son:</p>
{getData() && projects.map((proyecto) => <div>{proyecto.titulo}</div>)}
</div>
);
This useEffect basically says "any time projects changes, run getData()":
useEffect(() => {
getData();
}, [projects]);
And running getData() changes projects:
const getData = async () => {
//...
setProjects(allProjects);
//...
};
Thus the endless cycle.
It sounds like you just want to getData() once, when the component first loads. For that you can just use an empty dependency array:
useEffect(() => {
getData();
}, []);

How to stop useEffect from inifite loop when first setting state and then using state

There are plenty of questions about this topic already but none I could see that both set the state then use that same state.
I am setting state on the first run and then passing the state back in via the array. In theory, this should run only 2 times, once when data1 and data2 are blank and then again when they change to updated.
Passing in the state this way makes it run forever currently. How do I stop this? The data is not updating so why is it running over and over? Or do this another way?
I don't want combineData running each time a change is made as it is not necessary. I want this to take place on load only (like I am trying to do here).
const [data1, setData1] = useState('')
const [data2, setData2] = useState('')
const [data, setData] = useState('')
useEffect(() => {
///SET STATE
data1
.getData1(getData1Func)
.then((res) => setData1(res))
data2
.getData2(getData2Func)
.then((res) => setData2(res))
///USE STATE
combineData(data1, data2)
}, [data1, data2]) // pass in an array as a second argument
const combineData = (...args) => {
let data = [...args].flat()
setData(data)
}
You are updating the state, yet you are using those state as dependency, so it becomes a infinite loop.
If you only want to use it once then remove the dependencies.
useEffect(() => {
// your codes.
},[])
If not, then there's not necessary to use state, instead, use local variables.
useEffect(() => {
///SET STATE
const initiate = async () => {
const local1 = await getData1()
const local2 = await getData2()
combineData(local1, local2)
}
initiate();
}, []) // pass in an array as a second argument
const combineData = (...args) => {
let data = [...args].flat()
setData(data)
}

How to update state array fetched from API in React Hooks?

I'm fetching data from Studio Ghibli API and I am able to successfully fetch it, set the state array of objects and render it in my presentational component. However, I'm trying to create a function which will add new property "keyword" to every object in my state array. The problem is that when i try to copy the state array to manipulate it in my createKeywords function, the returned copy is empty and I'm unable to manipulate it after it being set.
This is the relevant code:
const baseUrl = 'https://ghibliapi.herokuapp.com/'
const [hasError, setErrors] = useState(false)
const [movies, setMovies] = useState([])
useEffect(() => {
fetch(baseUrl + 'films')
.then((res) => res.json())
.then((res) => {
console.log(res);
setMovies(res)
createKeywords()
})
.catch(err => setErrors(true));
}, [])
const createKeywords = () => {
const moviesWithKeywords = [...movies]
moviesWithKeywords.forEach(function(movie){
movie.keyword = 'castle'
});
setMovies(moviesWithKeywords)
}
If i don't call the createKeywords function everything works fine but obviously copying and setting new movies state creates problem. I tried adding [movies] instead of empty array in useEffect and that works but then useEffect runs indefinitely. Thank you in advance, React isn't my strong suit!
The solution seems might not be very obvious. There are cases where setMovies (in general setting the state) is an async operation, which means that even if you setMovies the movies variable is not being updated quite fast and therefore you are already executing the createKeawords function. This means that within the keywords function the movies variable didn't have the chance to update fast enough. I would recommend to pass the res as a parameter in the createKeywords and use this variable to copy the array to the moviesWithKeywords.
Have a look here under the section State Updates May Be Asynchronous
So do something like that:
const baseUrl = 'https://ghibliapi.herokuapp.com/'
const [hasError, setErrors] = useState(false)
const [movies, setMovies] = useState([])
useEffect(() => {
fetch(baseUrl + 'films')
.then((res) => res.json())
.then((res) => {
console.log(res);
setMovies(res)
createKeywords(res)
})
.catch(err => setErrors(true));
}, [])
const createKeywords = (movies) => {
const moviesWithKeywords = [...movies]
moviesWithKeywords.forEach(function(movie){
movie.keyword = 'castle'
});
setMovies(moviesWithKeywords)
}

Avoid useEffect to be triggered when one nested dependency changes

My form has 3 fields, lambda, period and filterPattern. When the values of lambda and period change, it will trigger handleSearch. However, I don't want handleSearch to be triggered when filterPattern changes.
If I don't put filterPattern in the dependency array, then handleSearch cannot get the latest value of filterPattern.
How to avoid useEffect to be triggered when one nested dependency changes? Thanks.
const getLogQuery = useCallback(() => ({
lambda,
period,
filterPattern,
}), [lambda, period, filterPattern]);
const handleSearch = useCallback(async () => {
await getLambdaLogs(getLogQuery());
}, [getLogQuery]);
useEffect(() => {
handleSearch();
}, [handleSearch]);
const getLambdaLogs = async (query) => {
const logs = await LambdaService.getLambdaLogs(query);
setLogItems(logs);
}
I'm making a few assumptions, but the second argument of useEffect are its dependencies, the things it watches to know when to fire. So instead of
useEffect(() => {
handleSearch();
}, [handleSearch]);
Which doesn't make any sense, to watch a function and then call that function if it changes, change that to
useEffect(() => {
handleSearch();
}, [lamba, period]);
```

Resources