3

I'm relatively new to React. I have a set of data that needs to be retrieved once and only once from the server, at start-up. The data is then displayed in my Footer component which is in App.js. I thought that useContext() might be the best way to go, but can't quite figure it out.

The code for initializing the data (an object) is something like this:

 axios.get('/url/to/get/data')
            .then((response) => {
                const myData = response.data.mydata;
                setMyData({ key1: myData.field1, key2: myData.field2, key3: myData.field3 });

            });

Right now, setMyData is a state, which sets the value of the object (which is constructed from various fields that were returned from the Axios call.

How do I initialize the context with this value.

I then have a Footer component, so I imagine I want to do something like this

<context.Provider>
   <Footer>
</context.Provider>

If useContext isn't the way to go, I'm open to idea. Essentially, looking for a singleton, that is initialized once, which the Footer component can than retrieve and render for every page.

Peter Kronenberg
  • 878
  • 10
  • 32

1 Answers1

2

You should define your context, and initialize it in a one-time useEffect. once it's loaded, all the children will be able to access the context using useContext

The "Trick" here is to use a useEffect with no dependencies, causing it to run only once (As long as its parent is not re-rendering)

In this example I'm using the context on the direct child (Main), but it can be used on a deeper level.

// initialize the context
const MyContext = React.createContext();

function App() {
  const [myData, setMyData] = React.useState(null);

  // Load your data once
  React.useEffect(() => {
    axios.get('/url/to/get/data')
            .then((response) => {
                const myData = response.data.mydata;
                setMyData({ key1: myData.field1, key2: myData.field2, key3: myData.field3 });
            });
  }, []);

  // Render the Provider and the app tree
  return (
    <MyContext.Provider value={myData}>
      <Main />
    </MyContext.Provider>
  );
}

// Consumer
function Main() {
  const myData = useContext(MyContext);
  if (!myData) {
    return <div>Loading...</div>;
  }

  return (
    <div>{myData}</div>
  );
}

gilamran
  • 7,090
  • 4
  • 31
  • 50
  • Thanks. I've tried using `useEffect`, but find that it re-executes every time the component renders (and since I have this in App.js, that's a lot). But let me take a closer look at your solution and hopefully, I'll have more luck. I originally tried doing it as a separate component that my Footer component used. The biggest conceptual issue I'm having with React is the fact that you have to have so much stuff in App.js, since all the data flows downward. So if `Comp A` and `Comp B` need to share data, you pretty much have to define it in App.js – Peter Kronenberg Nov 13 '21 at 19:59
  • 1
    Note that I've defined the `React.userEffect` with `[]` as the 2nd parameter, which guarantee one render. unless the `App` component was removed and re-added to the DOM. – gilamran Nov 13 '21 at 20:24
  • 1
    Using `context` to share data is a good start, but when doing a large application it becomes problematic to handle with large state. there are several solutions to state management, from Redux to MobX. read about them. – gilamran Nov 13 '21 at 20:25