ztefanie
ztefanie

Reputation: 850

Save value from custom hook to state

I am trying to save a value from a custom hook, which is fetching data for the server, to functional component state with useState, because I later need to change this value and after the change it needs to rerender. So desired behaviour is:

  1. Set State variable to value from custom hook
  2. Render stuff with this state variable
  3. Modify state on button click
  4. Rerender with new state

What I tried is:

What would be a proper solution for my use case?

Upvotes: 1

Views: 4951

Answers (1)

Oleksandr Kovalenko
Oleksandr Kovalenko

Reputation: 616

It looks like your custom hook returns a new array every time it is used.

Solution 1: change your hook to return a 'cached' instance of an array.

function useLoadData(id) {
  const [data, setData] = useState([]);

  useEffect(() => {
    loadData(id).then(setData);
  }, [id]);

  // good
  return data;

  //bad
  //return data.map(...)

  //bad
  //return data.filter(...)

  //etc
}

codesandbox.io link

Solution 2: change your hook to accept setData as a parameter.

function useLoadData(id, setData) {
  useEffect(() => {
    loadData(id).then(setData);
  }, [id]);
}

Here I am telling the hook where to store data so that both custom hook and a button in a component can write to a same place.

codesandbox.io link

Full example:

import React from "react";
import ReactDOM from "react-dom";
import { useState, useEffect } from "react";

// simulates async data loading
function loadData(id) {
  return new Promise((resolve) => setTimeout(resolve, 1000, [id, id, id]));
}

// a specialized 'stateless' version of custom hook
function useLoadData(id, setData) {
  useEffect(() => {
    loadData(id).then(setData);
  }, [id]);
}

function App() {
  const [data, setData] = useState(null);

  useLoadData(123, setData);

  return (
    <div>
      <div>Data: {data == null ? "Loading..." : data.join()}</div>
      <div>
        <button onClick={() => setData([456, 456, 456])}>Change data</button>
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("container"));

Upvotes: 3

Related Questions