Home > database >  react functional component Strange behavior of setHooks when I fetch data
react functional component Strange behavior of setHooks when I fetch data

Time:05-24

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?

  • Related