-2

I'm trying to set a useState variable using an interface inside a createContext. The goal is fetch some data inside someArray and added into a useState. The following code uses react with typescript:

import { createContext, useState } from 'react';

export interface exportedInterface {
  stateData: NftsData[];
  fetchUnstakedNFTs: () => void;
}

interface Data {
  dataString: string,
  dataNumber: number,
}

export const customContext = createContext<exportedInterface>();

const CustomContext: FC = ({ children = null as any }) => {
  const [stateData, setStateData] = useState<Data[]>([]);
  let listDataArray: Data[] = [];

  export const fetchSomeArray = async () => {
    for (let item of someArray) {
      await fetch(item.url)
        .then(resp =>
          resp.json()
        ).then((json) => {
          const listData: Data = {  
            {
              dataString: json.stringData,
              dataNumber: json.numberData
            }
          listDataArray.push(listData)
        }
    }
    setStateData(listDataArray) // this part doesn't work
  }

  console.log('stateData: ', stateData)

  return (
    <CustomContext.Provider
      value={{
        stateData,
        fetchUnstakedNFTs,
      }}
    >
      {children}
    </CustomContext.Provider>
  );
}

Then I create the context with the folowing file:

import { useContext } from 'react';
import { CustomContext } from 'aboveFile';

export const useCustomContext = () => {
  const context = useContext(CustomContext);
  if (context === undefined) {
    throw new Error(
      'CustomContext undefined'
    );
  }
  return context;
};

Finally in other component I import the CustomContext and run fetchSomeArray() using useEffect as in the following code:

import { useCustomContext } from 'aboveFile';

export default function App() {
  const { fetchSomeArray } = useCustomContext();
  useEffect(() => { fetchSomeArray() }, [])

  return (
   ...
  )
}

then I obtain the following output:

stateData: [] // HERE IS THE ISSUE

I don't know why useState setStateData is not working since stateData value is []

I tried to use await new Promise((resolve) => setTimeout(resolve, 1500)); before console.log('stateData: ', stateData) to give it time to update the state of stateData and still getting []

cris.sol
  • 161
  • 1
  • 1
  • 10
  • 1
    Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – HappyDev Jan 02 '23 at 15:47
  • 1
    Are you using `useState()` outside of a component ? It is not meant for that – OneQ Jan 02 '23 at 15:49
  • 1
    also the `listData.push` closing brackets are the wrong way around. – White Wizard Jan 02 '23 at 15:49
  • 1
    You are mutating state on subsequent re-renders by calling `push` on `listData` -- which is now a pointer to the same array as `stateData`. – Linda Paiste Jan 02 '23 at 15:51
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. – Community Jan 02 '23 at 16:06
  • @HappyDev I can't find the answer where you recommend, it is the same problem but in different scenarios. – cris.sol Jan 02 '23 at 16:59
  • @OneQ I'm inside a component (now I clarify it). – cris.sol Jan 02 '23 at 16:59
  • @Linda Paiste I tried your proposal without solving the issue (this is updated in the question) – cris.sol Jan 02 '23 at 17:00
  • 1
    That can't be the actual component, right? Because you would get an infinite loop ("too many re renders") if you are calling `setStateData` at the top-level like that. – Linda Paiste Jan 02 '23 at 17:02
  • @Linda Paiste You are right, I'm actually parsing an array to save its values in a useState (I updated the question) – cris.sol Jan 02 '23 at 17:16

4 Answers4

1

The following be a working version of what you were trying to do

import "./styles.css";

import { useState } from "react";

interface Data {
  dataString: string;
  dataNumber: number;
}

export default function App() {
  const [stateData, setStateData] = useState<Data[]>([]);

  const addToData = () => {
    setStateData((prev) => [
      ...prev,
      {
        dataString: "data",
        dataNumber: 2
      }
    ]);
  };

  console.log("stateData: ", stateData);

  return (
    <div className="App">
      <h1>Title Text</h1>
      <h2>Subtitle text</h2>
      <button onClick={addToData}>Press ME</button>
    </div>
  );
}

White Wizard
  • 608
  • 3
  • 19
0

If you want to directly update your state, you can use useEffect like this :

useEffect(() => {
    fetchSomeArray()
  }, []);

And you can remove the export of the function fetchSomeArray

OneQ
  • 1,149
  • 1
  • 8
  • 15
0

You haven't called FetchSomeDataArray() function anywhere that's why your state is still an empty array. If you want it to be called before rendering it then wrap this in the UseEffect hook with an empty dependency list.

or You can trigger this function on any event like button clicking, hovering etc.

Here is code for how you can use useEffect hook:

useEffect( async () => {
    // fetch some data function declaration
    const fetchSomeDataArray = async () => { .... };
    // function call
    await fetchSomeDataArray();
},[])
0

The issue is produced because the useState set method is not reflecting a change immediately as it is expained in this post The useState set method is not reflecting a change immediately

When I console.log the value of stateData after calling useEffect the value is correct.

cris.sol
  • 161
  • 1
  • 1
  • 10