Unusual behavior of setHooks in React Functional Components during data retrieval

Initially, I had a React class that was fulfilling its purpose perfectly. However, I had to convert it into a functional component in order to utilize the useLocation() hook.

After making this change, I encountered an issue where the state updates were not functioning as expected anymore.

The current structure of my function is as follows:

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;

Within the fetchData function, there seems to be a problem where dataFetch array size needs to be known in the .then block but it remains undefined at runtime. Creating a copy within the same function helps define it. This behavior is puzzling.

I attempted to move the Axios call out of fetchData by creating an asynchronous function that returned a promise, but the outcome remained similar.

I even tried making the fetchData function synchronous without the try/catch block, yet the issue persisted.

My ultimate goal is to pause the flow in the fetchData function inside the .then block, populate dataFetch, check the array size, and then render the component. Avoiding setTimeout for such behavior because it appears too static. Is there an alternative method?

If anyone can identify what could be wrong in this code causing this behavior, your assistance would be greatly appreciated. Thank you in advance.

Answer №1

To improve the code, consider moving the setSomeBoolean section into a separate useEffect function that triggers when the value of dataFetch changes. Here's an example:

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;

Questioning why you included

[setDataFetch, setLoading, setSomeBoolean]
in the dependency array of the initial useEffect.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue: Generated JavaScript files not visible in Visual Studio when using TypeScript.Explanation: When working with

Is there a way to locate the JavaScript files generated from the TypeScript file in Visual Studio 2015? It seems that although the JavaScript files are present in File Explorer, they are not visible in the solution explorer. I attempted to add the _refer ...

Defining Multiple Types in Typescript

I have created an interface in a TypeScript definition file named d.ts: declare module myModule { interface IQedModel { field: string | string[]; operator: string; } } In an Angular controller, I have utilized this interface like ...

Encountering npm3 installation errors with typyings in Angular 2?

Each time I try to sudo npm install Angular 2 modules, everything updates and installs correctly. However, I encounter the following error when the typings install is attempted: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0 ...

Using Angular2 to import components and services from a module

I am currently developing a final Angular2 application with two modules: CoreModule: This module includes shared components and services. AppModule: The main module of the application. AppModule: /** * Created by jamdahl on 9/21/16. */ // Imports impo ...

How can I adhere to Angular 2's naming convention for Input and Output as suggested by the styleguide?

Working with inputs and outputs while following Angular 2's styleguide naming convention Initially, my directive was defined like this: ... inputs: [ 'onOutside' ] ... export class ClickOutsideDirective { @Output() onOutside: EventEmitter ...

Ensuring strictNullChecks in Typescript is crucial when passing values between functions

When using the --strictNullChecks flag in TypeScript, there seems to be an issue with inferring that an optional property is not undefined when the check occurs in a separate function. (Please refer to the example provided, as articulating this clearly is ...

Synchronizing Data From Axios Request With Firebase Storage - Vue.js

After successfully receiving the desired response from an axios call to an API, my next task is to push that data into my Firebase Firestore. While I am confident in how to push data into Firestore, I am facing difficulty accessing the data correctly from ...

How to transform Observable<string[]> to Observable<string> using RxJS

Currently, I am facing the task of converting an Observable<string[]> to an Observable<string> I'm uncertain whether the mergeAll operator is the solution for this scenario. const o1: Observable<string[]> = of(['a', &apos ...

The closeOnClickOutside feature seems to be malfunctioning in the angular-2-dropdown-multiselect plugin

I'm currently using 2 angular-2-dropdown-multiselect dropdowns within a bootstarp mega div. The issue I'm facing is that when I click on the dropdown, it opens fine. However, when I click outside of the dropdown, it doesn't close as expected ...

When trying to access the key value of a dynamically generated object, it returns as undefined

I am facing a challenge with my student object structure... { Freshmen: [{id: 3}, {id: 5}], Sophomores: [{id: 2}, {id: 6}], Juniors: [{id: 1}, {id: 8}], Seniors: [{id: 9}, {id: 4}, {id: 7}] } My goal is to retrieve full student objects from the d ...

Attempting to resolve the issue of "Unable to locate a declaration file for module" in the aws-appsync library has led to the emergence of yet another error

While utilizing the aws-appsync library, I encountered the following error: (4,42): Could not find a declaration file for module '@redux-offline/redux-offline/lib/types'. 'node_modules/aws-appsync/node_modules/@redux-offline/redux-offline ...

Is there a method to guarantee that child components execute actions following parent components?

While working on my application, I encountered an issue when attempting to trigger an action within the useEffect hook that is defined at the root of the application. Further down the component tree, I am using the useEffect hook to make API calls that rel ...

First attempt at calling the Angular REST service does not yield any returned value

I currently have 3 components in my Angular application: 1. LeftMenuComponent 2. AppComponent 3. SummaryComponent Additionally, I have two services - one for making REST calls and another for sharing data among components (RestService, ComponentService). ...

Create two distinct classes featuring varied decorator choices

I need to split a single class definition into two separate classes: one with nullable true, and the other with nullable false. Specifically, I'm trying to create different versions of PlanInput. const createExample = (nullable) => { @InputType ...

Repeating promises resolutions yields stagnant outcomes

Within my Angular project, I am working with two distinct components. parent.component.ts mypromise = this.httpClient.get<any>('http://localhost').toPromise() parent.component.html <app-children #child [promise]="mypromise"></a ...

Guidelines on implementing the map function to fetch data in ReactJS

Currently, I am working on structuring a homepage with the following setup: const Login: React.FC = () => { [ ... ] return ( <IonPage> <IonContent> <IonSlides pager={false} options={slideOpts}> ...

Error: Attempting to use a React hook in a non-functional component in

I developed a simple application for educational purposes that features a contact book allowing users to add and remove new contacts. The functionality is consolidated on one page as shown in the code snippet below: index import styles from '../style ...

Assigning an enum value from TypeScript to another enum value

One of the functions I've written is named saveTask type Task = string; enum Priority { Low = "Low", Medium = "Medium", High = "High", } enum Label { Low = "Low", Med = "Med", High = " ...

Can I implement react-router's useHistory() or React's useState method alongside withFormik?

Utilizing withFormik in my registration form. const RegistrationContainer = compose<InnerProps, OuterProps>( withFormik<InnerProps, RegistrationValues>({ mapPropsToValues: () => RegistrationSchema.cast(), validationSchema: Registra ...

Starting component by passing parameter retrieved from local storage in react/nextjs

My current challenge involves passing an access token from localStorage to a component provided by an external library. Despite my attempts using the useEffect hook, I am encountering errors like {code: "initialization-error", error: "Access ...