How exactly data in useEffect work on page load - reactjs

I am fetching data from Firebase when the page loads with useEffect. I want to use socialLinks state in the application like socialLinks.instagram but I am getting this error " Cannot read properties of undefined (reading 'instagram') " .When I assign socialLinks one by one and assign them in the useEffect and use in the page that doesn't give an error. Can anyone explain why I need to assign them one by one instead read from socialLinks.instagram .You can also find my firebase data image below.
const [socialLinks, setSocialLinks] = useState();
const [instagram, setInstagram] = useState();
const [tiktok, setTiktok] = useState();
const [youtube, setYoutube] = useState();
useEffect(() => {
const getSocialLinks = async () => {
const docRef = doc(socialLinksCol, "24STOxvBEmCezY16LD");
const fireStoreData = await getDoc(docRef);
setSocialLinks(fireStoreData.data()); // I can't use this data in <a>
setYoutube(fireStoreData.data().youtube); // I can use this data in <a>
setInstagram(fireStoreData.data().instagram);
setTiktok(fireStoreData.data().tiktok);
};
getSocialLinks();
}, []);
console.log(socialLinks)
//output : {instagram: 'https://www.instagram.com/', youtube: 'https://www.youtube.com/', tiktok: 'https://www.tiktok.com/en/'}
//This is where I use the socialLinks in
<a href={socialLinks.instagram}>Instagram</a> // This give error
<a href={tiktok}>Tiktok</a> // This is fine
<a href={youtube}>Youtube</a>

The first time your component renders you set up some empty state.
For example:
const [socialLinks, setSocialLinks] = useState();
results in a variable called socialLinks being declared. And since you passed no argument to useState, socialLinks will be undefined.
Now useEffect will run. It will kick off an async action in the background (not blocking the component from rendering).
While the data is fetching your component will try render the first time. You reference socialLinks.instagram and socialLinks is undefined so you get an error.
You probably want to not render anything until the data has been loaded.
There are multiple ways to do that. If you want the code to work with the html you have right now, just declare the initial state as empty for social links like this:
const [socialLinks, setSocialLinks] = useState({
instagram: '', tiktok: '', youtube: ''
});
If you just want to check if the data is there yet then you can leave the state as undefined and then check if its available yet when rendering:
if (socialLinks && socialLinks.instagram) {
return <a href={socialLinks.instagram}>Instagram</a>;
} else {
return null;
}
Remember that calling setSocialLinks causes a state update so it will re-render the component when the data is there!

Related

react doesn't render latest value

For the initial render, I have object date, which is an empty array. I then try to get data from an influxDB, but the get result isn't reflected by React with a re-render. The get function is calling in useEffect (you can see this in screenshots). I use typescript, and to avoid getting an error on the initial load (that data is an empty array of objects and it doesn't have a value property) I use the typescript syntax, but it still doesn't display the correct value. It doesn't display anything at all.
What could be the problem? In the last photo, we can see another way to display data without a question mark from typescript, but it doesn't work correctly either, even if the length of the array is greater than 0, it still doesn't display data[0].value.
Initial data:
Data after DB get:
Get the first element in array:
Browser result (before ':' we should see data[0].value):
Alternate way (when data isn't empty we should see the value of the first object in array):
I also show we code
import React from 'react';
import './App.css';
import { FluxTableMetaData } from '#influxdata/influxdb-client';
const { InfluxDB } = require('#influxdata/influxdb-client');
export const App = () => {
debugger
const authData = {
token: 'Emyw1rqUDthYRLpmmBc6O1_yt9rGTT57O50zoKiXUoScAjL6G-MgUN6G_U9THilr86BfIPHMYt6_KSDNHhc9Jg==',
org: 'testOrg',
bucket: 'test-bucket',
};
const client = new InfluxDB({
url: 'http://localhost:8086',
token: authData.token,
});
const queryApi = client.getQueryApi(authData.org);
const query = `from(bucket: "${authData.bucket}") |> range(start: -1d)`;
const data: any[] = [];
React.useEffect(() => {
queryApi.queryRows(query, {
next(row: string[], tableMeta: FluxTableMetaData) {
debugger;
const o = tableMeta.toObject(row);
const item = {
time: o._time,
measurement: o._measurement,
field: o._field,
value: o._value,
};
return data.push(item);
},
error(error: Error) {
return error;
},
complete() {
console.log(data)
return data;
},
})
},[]);
debugger;
return (
<div>
<div>{data.length !== data[0].value}:</div>
<div>hello</div>
</div>
);
};
another way:
<div>
<div>{data[0]?.value}:</div>
<div>hello</div>
</div>
The main issue in your code is, You have defined data as a const variable, and not as a state. Thus, in useEffect, even if your data gets changed, it will not reflect on data[0].value as it is a const variable and react doesn't render updated values of variables. It updates/renders only if it's a state.
In short, Convert your const data to be a stateand use setState like below for your code to work!
const [data, setData] = React.useState([]);
...
setData([...data , item]);
I suggest you use the React States for that in the following way
var [nameOfVariableWhichWillChange, changeFunction] = React.useState("");
now whenever whichever function wants to change the value of that function just use changeFunction(newValueOfVariable)
the plus point of using React state is wherever you might have used that variable on change of That variable each instance will change on its own...
Do let me know does that solve your problem, or you need something else
React doesn't re-render the webpage even if the data has changed. You need to store your data inside a React.useState and call setState to trigger a re-render.
const [data, setData] = useState([])
React.useEffect(() => {
...
next(row: string[], tableMeta: FluxTableMetaData) {
...
setData([...data, item])
},
...
Read about useState here for more information: https://reactjs.org/docs/hooks-state.html

My code doesn't fetch data until after a re-render

So i have been trying to fetch data from an API and the link was dependent on a url parameter, but the data is fetched only after a re-render and i cant access them in my render function.
It works on normal pages but when i try to fetch data inside a route depending on a parameter passed with that route, it doesnt work. How could i solve this problem?
NOTE: my fetch is inside a different component with a different file from the one im passing the parameter with
const { name } = useParams();
const [countries, setCountries] = useState([]);
const fetchData = async () => {
const { data } = await axios.get(`https://restcountries.eu/rest/v2/name/${name}`);
setCountries(data);
};
useEffect(() => {
fetchData();
}, []);
Its normal behavior as you and sending an async call to fetch data, and at that time 1st render is called,
To handle this you should add a new state called loading and set it true before calling API and set it false once you got data,
and on bases on loading state, show some loader in ur render method
First of all, you should set dependencies array in your useEffect, so for now, you've set it as empty [], and it means that it will execute only the first render. So, as I understood, you wanna execute ur fetchData function every 'name' params update, so you need to add that param to ur useEffect function, this way:
useEffect(() => {
fetchData();
}, [name]);
Hope I've understood ur request correctly.
I was trying access the fetched data before specifying the index and I didn't use map.

Set state on changed dependencies without additional render (having a loader without additional renders)

I have a huge component which renders long enough to be noticeable by user if it happens too often.
Its contents are loaded asynchronously (from a server) every time when a function "getData" changes, showing a loader during the wait.
I'm trying to write a code which will render the component only 2 times when the function changes - first time to show the loaded and the second time to display the data.
Using a standard useEffect causes it to be rendered 3 times, first of which doesn't change anything visible for the user.
type tData = /*some type*/
const Component = (props: {getData: () => Promise<tData[]>}) => {
const {getData} = props;
const [loader, setLoader] = useState(true);
const [data, setData] = useState<tData[]>([]);
useEffect(() => {
setLoader(true);
setData([]);
getData().then((newData) => {
setData(newData);
setLoader(false);
});
}, [getData, setLoader, setData]);
// ... the rest of the component (that doesn't use the function getData)
};
The three renders are:
getData has changed - the effect runs and changes the states but there is 0 changes visible to the user (this is the render I want to get rid of)
loader has changed to true and data has changed to [] - a useful render that actually changes the UI
loader has changed to false and data has changed to a new value - a useful render that actually changes the UI
How could I modify this code to not have a barren render when getData changes?
this is a very common use case.
what you can do here is a if() in the useEffect().
using a .then() complicates things. use async await to make it asynchronus.
useEffect(() => {
async function loadData() {
const res = getData()
return res
}
let data = await loadData()
//check if it's undefined
if (data && data!=='init') {
setStateData(data)
}
}, [])
this is how i'd implement something like this. however it seems like the data fetching for you is somewhat complicated then this, but essentially, never fetch data synchronously unless you absolutely have to, and check if it's undefined in useEffect before setting the state variable so that it's only set once.

useState() losing the state(data) on refresh when the data is coming from redux store

I am having a weird issue that makes the data set to undefined. I am implementing react-table (one with hooks). If I use one from the example of react-table, they make use makedata.js & their code is below
const [data, setData] = useState(useMemo(() => makeData(20), []) );
But my code just replaces makeData with my data that is coming from redux after API call.
//For setting data
useEffect(() => {
if (!currentUser || !savedProjectId) {
alert("Please Select A Project");
return;
}
listenToProjectData(currentUser.clientId, savedProjectId);
}, [currentUser, savedProjectId, listenToGuests]);
// For setting state
const [data, setData] = useState(
useMemo(() => projectData, [projectData, loading])
);
So when we make a change in code, react renders and data is coming fine, i.e data is not undefined. It's the same when we go to other page and navigate back to it.
The moment we refresh the page, data is undefined.
P.S. projectData is not undefined, only, in the beginning, momentarily it is but then the data updates in it but it is not reflected in our state of data & refresh makes the data undefined though we are using useMemo.

Howcome my state is not updating using react hooks and use Effect

My useEffect function is trying to fetch data from an API endpoint. The results resultAllTestTypes are currently logging fine.
However, I can't find out why the allTestTypes are coming back as undefined as I thought I had already set it in a state variable it should be able to log it to the console. But when I log the allTestTypes data it gives me this.
Code:
const [allTestTypes, setAllTestTypes] = useState([])
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
console.log('resultAllTestTypes data ',resultsAllTestTypes.data);
console.log('allTestTypes data ',allTestTypes.data);
}
onLoadCreateUnitTests()
It's setting the state, you just have a console.log in a spot that's not particularly useful.
allTestTypes is a local const. It will never change, and that's not what setAllTestTypes is trying to do. When you set state, this tells react to render the component again. When that render occurs, you'll make a new call to useState, which will return the new value and assign it to a new local const. That new variable can be interacted with by code in the new render, but code from the previous render (such as your console.log) will never see the new value.
If you'd like to verify that the component is rerendering with a new value, move your console.log into the body of the component:
const [allTestTypes, setAllTestTypes] = useState([])
console.log('Rendering with', allTestTypes);
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
}
onLoadCreateUnitTests()
});
cuz setAllTestTypes is async, so u can't get it immediately.
if u want to use it ,use the local variable resultsAllTestTypes instead

Resources