Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Hooks - How to use a make a state dependant on another state?

I recently started using hooks in react and I am often having this problem: I create a first big state which is used by all my component, but some of the smaller parts of my component divide this state and create their own state for simplicity.

For example

import React, { useState } from "react";

const initialFilters = {
  name: "",
  code: ""
};

function Filter({ value, setFilters }) {
  const [tempValue, setTempValue] = useState(value);

  return (
    <input
      value={tempValue}
      onChange={e => setTempValue(e.target.value)}
      onBlur={() => setFilters(tempValue)}
    />
  );
}

function App() {
  const [filters, setFilters] = useState(initialFilters);
  const agents = [
    { name: "bob", code: "123" },
    { name: "burger", code: "3123" },
    { name: "sponge", code: "34" }
  ];

  return (
    <div>
      <label>Name filter</label>
      <Filter
        value={filters.name}
        setFilters={value =>
          setFilters(filters => ({ ...filters, name: value }))
        }
      />
      <label>Code filter</label>
      <Filter
        value={filters.code}
        setFilters={value =>
          setFilters(filters => ({ ...filters, code: value }))
        }
      />
      <button onClick={() => setFilters(initialFilters)}>Reset filters</button>
      <ul>
        {agents
          .filter(
            agent =>
              agent.name.includes(filters.name) &&
              agent.code.includes(filters.code)
          )
          .map((agent, i) => (
            <li key={i}>
              name: {agent.name} - code: {agent.code}
            </li>
          ))}
      </ul>
    </div>
  );
}

export default App;

CodeSandox available here

In this example the filters work fine, but their value are not emptied when we use the button Reset.

The filters create their own states to dispatch new state only on blur, and still be controlled. I guess I could use ref here, but I use this example to showcase a simple case of state dependent on another state (and therefore on props).

How should I go about implementing this in an idiomatic React way?

like image 997
balthazar Avatar asked Jan 25 '23 06:01

balthazar


2 Answers

As you can read in the documentation (https://reactjs.org/docs/hooks-state.html#declaring-a-state-variable), your state is created on the first render only and only then it is equal to your initial value. You could write a custom hook useFilter and expose your filter-reseter:

const useFilter = (value, setFilters) => {
  const [tempValue, setTempValue] = useState(value);
  const resetFilter = () => setTempValue(value)

  return {
      resetFilter,
      getInputProps: () => ({
          onChange: e => setTempValue(e.target.value),
          onBlur: () => setFilters(tempValue),
          value: tempValue,
      })
  }

and instead of doing:

      <Filter
        value={filters.name}
        setFilters={value =>
          setFilters(filters => ({ ...filters, name: value }))
        }
      />

do this:

const setFilters = value => setFilters(filters => ({ ...filters, name: value }))
const { resetTempFilter, getInputProps } = useFilter(value, setFilters)
...
<input {...getInputProps()} />
like image 34
Khalt Avatar answered Feb 03 '23 06:02

Khalt


You can use a useEffect hook. The 1st argument is a function and the 2nd argument is an array of dependencies. When a dependency changes value, the function is executed again.

import { useEffect } from 'react';

// ....code removed....

  useEffect(() => {
    setTempValue(value);
  }, [value]);

// ....code removed....

Sandbox with changes: https://codesandbox.io/s/kind-bogdan-ljugv

like image 126
WGriffing Avatar answered Feb 03 '23 06:02

WGriffing