useEffect() function runs only once, store is updated later (React hooks) - reactjs

I have an application where I am fetching the store as such:
const user_data = useSelector(state => state.user_data);
I have a useEffect() function right after that that assigns certain state objects to the data received from the store.
useEffect(() => {
setName(user_data.name);
}, []);
The store is being set by something like:
await dispatch(save_user_info(res.data.response));
props.history.push("/setting/dashboard");
Now the problem is, user_data is empty when useEffect first runs.
It gets populated afterwards, but useEffect just runs once.
The page is redirected to the new component before the store is updated, so when the page loads first, it does not have the store data.
How can I make useEffect run again if the data from the store is returned?

if you keep the 2nd argument of useEffect empty it will run every time the component updates.
useEffect(() => {
setName(user_data.name);
});
in your case it runs if variables mentioned in this array [](which is empty) are changed.
or you can do this
useEffect(() => {
setName(user_data.name);
},[user_data]); // or props.user_data , howsoever you can refer to that

Related

useEffect shows double result after back button click

I have a program where I use useeffect to get products I have in my database. I use useeffect with an empty array as a dependency, so the data only loads when the component is rendered. The problem is that if I change the page and right after use the browser's back button, the component shows double the products.
code
useEffect(() => {
dispatch(fetchProducts());
}, []);
Since you dispatch a redux action, it seems that you store the fetched products on redux. The redux state is not affected by route changes and is stored throughout the app until cleaned manually.
So what you can do is, you can define another action named cleanUpProducts in your reducer and within this function, you can clean up stored products in the redux store.
After that, you can dispatch this action in return function of your useEffect (the equivalent of componentWillUnmount) hook like:
useEffect(() => {
dispatch(fetchProducts());
return () => {
dispatch(cleanUpProducts());
}
}, []);

React useEffect run conditionally

How do I prevent my React component from fetching images every time the component is rendered and to fetch them from the store instead? I don't want to get rate limited by doing the API call over and over again, but the problem with useEffect is that it seems to be unaware of the variables being set "outside" of the effect. It seems to completely ignore !images.length and when I log the value for images.length it is always 0 :(
Images.tsx
const dispatch = useDispatch();
const images = useSelector(selectedImages);
useEffect(() => {
if (!images.length) {
dispatch(fetchImages());
}
}, []);
With React hook useMemo you will only call the API whenever you need it
https://www.robinwieruch.de/react-usememo-hook
https://reactjs.org/docs/hooks-reference.html#usememo
It is always logging 0 is because you didn't put anything inside dependency array of useEffect so that whenever React re-render your component, it looks inside the useEffect and see nothing inside the dep array, so it just skip running that useEffect.
When you use useEffect, you should declare dependencies in the array of useEffect that are used inside useEffect
const dispatch = useDispatch();
const images = useSelector(selectedImages);
useEffect(() => {
if (!images.length) {
dispatch(fetchImages());
}
}, [images]); // Our deps
So, let's say:
First, by default it will run the useEffect and do all the logics inside that effect. If the images array is empty, calling the API to get images.
Second, images array is not empty anymore, and the 2nd images is different from 1st images array which is empty, so it still run to the useEffect. But it won't fetch images anymore because you have if condition there.

REST API inside React.useEffect to run on each refersh of page

I have put a RESTFUL API inside my react useeffect hook by using axios.get() method, I need this REST API function is fetched and run on each refresh of the page?
Actually when, I am testing my application it is jus running once and no longer it updates
My react useeffect is like below
React.useEffect(() => {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
axios.get('http://127.0.0.1:9000/api/is_logged_in/')
.then(res =>{
console.log(res);
if (res.status!=200) {
throw new Error('Network response was not ok');}
return res;})
.then(res=>{
const value=res.data.res;
set_is_logged_in(value);
}).
catch(error=>{
console.log(error);
});});
I need this API to get re-run and fetched from the sever on each refresh of the page. How to achieve such functionality in reactjs?
Try to understand the concept:
useEffect(() => {
// your logic here which will run only one time when this component mounted
});
useEffect(() => {
// your logic here which will run every time when the variable which is passed in dependency array change
}, []); // Blank Dependency array
useEffect(() => {
// your logic here which will run every time when the variable which is passed in dependency array change
}, [variable1, variable2]); // Dependency array
Explanation:
Giving it an empty array acts like componentDidMount as in, it only
runs once.
Giving it no second argument acts as both componentDidMount and
componentDidUpdate, as in it runs first on mount and then on every
re-render.
Giving it an array as second argument with any value inside, eg ,
[variable1] will only execute the code inside your useEffect hook
ONCE on mount, as well as whenever that particular variable
(variable1) changes.
Reference

Explanation needed: getting data from API with useEffect hook and get name

const [ countries, setCountries ] = useState([])
const hook = () => {
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
setCountries(response.data)
})
}
useEffect(hook, [])
This one below doesn't work:
//Uncaught TypeError: Cannot read property 'name' of undefined
console.log(countries[1].name)
This one below does work:
<ul>
{countries.map(country => (
<li>{country.name}</li>
))}
</ul>
Any ide why one method of printing name does work, while the other doesn't?
Coz you can loop through the empty array, but you can't access the index which is not available yet
// So if
countries = []
// this will not throw error
{countries.map(country => (
<li>{country.name}</li>
))}
// but this will
console.log(countries[1].name)
// if you want to check try to run this
console.log(countries.length ? countries[1].name : "not available yer");
The usage of useEffect hook notifies React that component has to perform some side-effects(passed as a callback function to the hook) after it has been rendered, The default behavior of useEffect will run both after the first render and after every update, but when an empty array is passed as a dependency the side-effect will be performed only once after the component has been mounted for the first time.
In the case above useEffect(hook, []) the callback hook will be called after the component has mounted for the first time, which means the component will render with the initial state on it's first render which is an empty array ([]).
That is why when you try to access countries[1].name it errors out, because the value of countries is still an empty array on the first render.
const [ countries, setCountries ] = useState([])
const hook = () => {
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
setCountries(response.data)
})
}
useEffect(hook, [])
// can not use index expression to get the first element because
// the value of countries is still an empty array on first render
// it only gets populated when axios.get call is succesful inside the
// callback in useEffect hook after the component has mounted for the first time
console.log(countries[1].name)
Solution
Check for the length of the array before trying to get the first element,
if (countries.length) {
console.log(countries[1].name)
}
P.S.- You should be using a .catch block for handling the error when the API call fails.
There is an example solution for a type of request like this in the React document:
https://reactjs.org/docs/hooks-effect.html
The hooks provided by React are for the most part, asynchronous functions provided by React, to help manage the loading of data, presenting it to the DOM, and dealing with updates. The useEffect behaves in a similar way to componentHasLoaded, where the hook is triggered once the functional component has rendered, and the DOM has been loaded, but it may not have been presented to the user yet. It's important to remember this when working with useEffect. useState is another asynchronous hook, but it provides access to the state property of the hook after it has been instantiated, and won't immediately trigger a re-render of the component, unless the data is updated.
The reason you get an undefined error when you attempt to access console.log(countries[1].name) is because the array at that point is still empty.
I'll explain in code:
const myComponent = () => {
// initialise countries: []
const [ countries, setCountries ] = useState([])
const hook = () => {
axios
.get('https://restcountries.eu/rest/v2/all')
.then(response => {
// This is allow you to see the DOM change after the effect has run
setTimeout(() => setCountries(response.data), 5000);
})
}
// Tell react to run useEffect once the component is loaded
useEffect(hook, [])
// Display data
return (
<p>Countries: {countries.length}<p>
);
};
Because useEffect is an asynchronous function, it doesn't block the execution of the function and the rendering of the DOM, but refreshes the DOM once useEffect is completed. In this case, you are setting the country list, based on the result of the useEffect function.
The useEffect function will still trigger, you will have access to the state, and the function will re-render when the state is updated.
See codepen example:
https://codepen.io/jmitchell38488/pen/OJMXZPv

Setting Redux state as a default state when using React Hooks

I have a redux action that fetches all data and stores it into a global Redux store.
I want to store that state in a local state using Hooks so that the actual state doesn't get changed when I change it locally.
What I am doing right now is,
const [filteredTable, setFilteredTable] = useState([])
useEffect(() => {
props.fetchDatabase();
props.fetchOptions();
setFilteredTable(props.filtered_table_data);
}, [])
In useEffect, the props.fetchDatabase() gets the props.filtered_table_data and I can see that when I console.log it out.
However, when I use Hooks to store it into a local state and check if it's in there,
console.log(filteredTable, 'filteredTable')
just gives me [].
What am I doing wrong?
I believe the props.fetchDatabase() call is asynchronous, so by the time you are attempting to setFilteredTable the props.filtered_table_data has not updated yet.
You can try something like this:
useEffect(() => {
props.fetchDatabase();
props.fetchOptions();
}, [])
useEffect(() => {
setFilteredTable(props.filtered_table_data);
}, [props.filtered_table_data]);
Note that this effect will run every time filtered_table_data changes, so you may need to wrap around the code in the callback with some sort of condition if you want to restrict setting the local state.
useEffect's callback with [] as hook's second argument is only being called once when component just mounted. Inside it fetchDatabase, and fetchOptions callbacks are called, and right after that (when data isn't yet fetched) you call setFilteredTable, that's why there are empty array occurs in filteredTable.
Not sure if this answers your question, but React-Redux provides some hooks for accessing the redux store.
The one that might be of interest to you is the useSelector() hook.
Here's an example usage:
import { useSelector } from 'react-redux';
const App = () => {
const tableData = useSelector(state => state.tableData);
...
}

Resources