I had a React class that did the job I needed and I had to change it to a functional component to use useLocation().
It turns out that by switching to a functional component the update of my states does not work normally anymore.
My function is in this form
import * as React from "react";
import { useEffect, useState } from "react";
import Axios from "axios";
import { useLocation } from "react-router-dom";
const URL = `my-path`;
const Sessions: React.FC<{ loadingParam?: boolean | undefined, someBooleanParam?: boolean | undefined, data?: AnyType[] | undefined }> = ({ loadingParam = true, someBooleanParam = undefined, data = undefined}) => {
const [loading, setLoading] = useState(loadingParam);
const [someBoolean, setSomeBoolean] = useState(someBooleanParam);
const [dataFetch, setDataFetch] = useState(data);
const location:any = useLocation();
useEffect(() =>
{
if(location.state == null){
console.log("first implementation");
} else {
const dataToFill:AnyType[]|undefined = location.state.data;
if( data ) {
setDataFetch(dataToFill);
setLoading(false);
setSomeBoolean(undefined);
}
}
}
,
[setDataFetch, setLoading, setSomeBoolean]
);
async function fetchData() {
try {
setLoading(true);
await Axios.get<AnyType[]>(URL)
.then(res => {
if (res.status === 200 && res != null) {
console.log("1 ", dataFetch); //undefined
console.log("1 bis ", res.data); //the data I want in the form I want
var copy:AnyType[] = [...res.data];
setDataFetch([...res.data]);
console.log("2 ", dataFetch); //undefined
console.log("2 ter ", copy); //the data I want in the form I want
setDataFetch(copy);
console.log("2 ", dataFetch); //undefined
setLoading(false);
if (dataFetch?.length === parseInt(someValue)){
setSomeBoolean(true);
} else {
setSomeBoolean(false);
}
} else {
console.log('problem when fetching the data from backend');
}
})
.catch(error => {
console.log(error);
});
}
catch (exception) {
console.log(exception);
}
}
console.log("8 ", dataFetch); //Appears in the web console several times during the execution of the fetchData function with the data I want
return(
<div className="container">
<div>
<button onClick={fetchData}> Click to get data </button>
</div>
{(someBoolean == true && loading == false) ?
Some Action
: <></>
</div>
);
}
export default Sessions;
In this code, in the fetchData function, I need to know the size of the dataFetch array to set the value of someBoolean, but at run time dataFetch is not defined in the try block. But when I create a copy in this same function it is well defined, I don't understand it.
I also tried to get the call to Axios out of my fetchData function by putting it in an asynchronous function that returned a promise but it gave me a similar result.
I also tried to make the fetchData function non-asynchronous without the try/catch block but still the same result.
What I will need is that in the fetchData function, in the .then block the flow stops to fill my dataFetch then check the size of the array and then render my component. But I don't want to put a setTimeout because it's a too static behavior I think. Is there another way to do it?
Does anyone see what I am doing wrong in this code that would explain this behavior?
Thanks in advance for anyone who would take the time to help me
CodePudding user response:
One way would be to put the setSomeBoolean
part into another useEffect
which runs when the dataFetch
changes. Something like that:
import * as React from "react";
import { useEffect, useState } from "react";
import Axios from "axios";
import { useLocation } from "react-router-dom";
const URL = `my-path`;
const Sessions: React.FC<{ loadingParam?: boolean | undefined, someBooleanParam?: boolean | undefined, data?: AnyType[] | undefined }> = ({ loadingParam = true, someBooleanParam = undefined, data = undefined}) => {
const [loading, setLoading] = useState(loadingParam);
const [someBoolean, setSomeBoolean] = useState(someBooleanParam);
const [dataFetch, setDataFetch] = useState(data);
const location:any = useLocation();
useEffect(() =>
{
if(location.state == null){
console.log("first implementation");
} else {
const dataToFill:AnyType[]|undefined = location.state.data;
if( data ) {
setDataFetch(dataToFill);
setLoading(false);
setSomeBoolean(undefined);
}
}
}
,
[setDataFetch, setLoading, setSomeBoolean]
);
useEffect(()=>{
if (dataFetch?.length === parseInt(someValue)){
setSomeBoolean(true);
} else {
setSomeBoolean(false);
}
setLoading(false);
},[dataFetch])
async function fetchData() {
try {
setLoading(true);
await Axios.get<AnyType[]>(URL)
.then(res => {
if (res.status === 200 && res != null) {
console.log("1 ", dataFetch); //undefined
console.log("1 bis ", res.data); //the data I want in the form I want
var copy:AnyType[] = [...res.data];
setDataFetch([...res.data]);
console.log("2 ", dataFetch); //undefined
console.log("2 ter ", copy); //the data I want in the form I want
setDataFetch(copy);
console.log("2 ", dataFetch); //undefined
} else {
console.log('problem when fetching the data from backend');
}
})
.catch(error => {
console.log(error);
});
}
catch (exception) {
console.log(exception);
}
}
console.log("8 ", dataFetch); //Appears in the web console several times during the execution of the fetchData function with the data I want
return(
<div className="container">
<div>
<button onClick={fetchData}> Click to get data </button>
</div>
{(someBoolean == true && loading == false) ?
Some Action
: <></>
</div>
);
}
export default Sessions;
Also im not sure why you want [setDataFetch, setLoading, setSomeBoolean]
as a dependency array of the first useEffect
?