У меня есть набор кнопок в дочернем компоненте, где при нажатии установить соответствующее значение состояния true или false. У меня есть хук useEffect в этом дочернем компоненте, также с зависимостями от всех этих значений состояния, поэтому, если кнопка нажата, этот хук затем вызывает setFilter, который передается как опора от родителя ...

const Filter = ({ setFilter }) => {

  const [cycling, setCycling] = useState(true);
  const [diy, setDiy] = useState(true);

  useEffect(() => {
    setFilter({
      cycling: cycling,
      diy: diy
    });
  }, [cycling, diy]);

  return (
    <Fragment>
      <Row>
        <Col>
          <Button block onClick={() => setCycling(!cycling)}>cycling</Button>
        </Col>
        <Col>
          <Button block onClick={() => setdIY(!DIY)}>DIY</Button>
        </Col>
      </Row>
    </Fragment>
  );
};

В родительском компоненте я отображаю список элементов. У меня есть два эффекта в родительском, один из которых выполняет начальную загрузку элементов, а затем другой, который срабатывает при смене фильтра. Я удалил большую часть кода для краткости, но я думаю, что проблема, которую я имею, сводится к тому факту, что при рендеринге моей ItemDashboard фильтр вызывается дважды. Как я могу остановить это, или есть другой способ, которым я должен смотреть на это.

const ItemDashboard = () => {

  const [filter, setFilter] = useState(null);

  useEffect(() => {
    console.log('on mount');
  }, []);

  useEffect(() => {
    console.log('filter');
  }, [filter]);

  return (
    <Container>..
      <Filter setFilter={setFilter} />
    </Container>
  );
}
1
jonesy 21 Янв 2020 в 15:39

2 ответа

Лучший ответ

Полагаю, вы ищете способ поднять изложить общему родителю.

Для этого вы можете привязать обработчики событий дочерних компонентов (передаваемых как реквизиты) к желаемым обратным вызовам в их общем родительском элементе.

Следующая демонстрация демонстрирует концепцию:

const { render } = ReactDOM,
      { useState } = React
      
const hobbies = ['cycling', 'DIY', 'hiking'] 

const ChildList = ({list}) => (
  <ul>
    {list.map((li,key) => <li {...{key}}>{li}</li>)}
  </ul>
)
      
const ChildFilter = ({onFilter, visibleLabels}) => (
  <div>
  {
    hobbies.map((hobby,key) => (
      <label {...{key}}>{hobby}
        <input 
          type="checkbox" 
          value={hobby}
          checked={visibleLabels.includes(hobby)}
          onChange={({target:{value,checked}}) => onFilter(value, checked)}
        />
      </label>))
  }
  </div>
)
      
const Parent = () => {
  const [visibleHobbies, setVisibleHobbies] = useState(hobbies),
        onChangeVisibility = (hobby,visible) => {
          !visible ?
          setVisibleHobbies(visibleHobbies.filter(h => h != hobby)) :
          setVisibleHobbies([...visibleHobbies, hobby])       
        }
   return (
      <div>
        <ChildList list={visibleHobbies} />
        <ChildFilter onFilter={onChangeVisibility} visibleLabels={visibleHobbies} />
      </div>
   )
}

render (
  <Parent />,
  document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
1
Yevgen Gorbunkov 21 Янв 2020 в 13:32

Да, вы можете, useEffect в дочернем компоненте, который зависит от состояния, также как вы обычно реализуете компонент, который контролируется и не контролируется:

const NOOP = () => {};

// Filter
const Child = ({ onChange = NOOP }) => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    onChange(counter);
  }, [counter, onChange]);

  const onClick = () => setCounter(c => c + 1);

  return (
    <div>
      <div>{counter}</div>
      <button onClick={onClick}>Increase</button>
    </div>
  );
};

// ItemDashboard
const Parent = () => {
  const [value, setState] = useState(null);

  useEffect(() => {
    console.log(value);
  }, [value]);

  return <Child onChange={setState} />;
};

Edit stoic-snow-snu7o

1
Dennis Vash 21 Янв 2020 в 12:46