У меня есть базовая выпадающая настройка, как это:

(Очевидно, что в реальном мире fetchValues() пойдет на сервер):

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

const fetchValues = () => Promise.resolve(["option1", "option2"]);

function Dropdown(props) {
  const [options, setOptions] = useState(null);
  useEffect(() => {
      props.fetchValues().then(setOptions);
  }, [props.fetchValues]);

  return <select>{options && options.map(o => <option>{o}</option>)}</select>;
}

ReactDOM.render(
  <Dropdown fetchValues={fetchValues} />,
  document.getElementById("root")
);

Это прекрасно работает, но только один раз. Какой идиоматический способ заставить компонент снова вызывать fetchValues(), когда что-то еще меняется в моем приложении? (Также допустимо, если fetchValues() вызывается при каждом рендеринге, кроме тех, которые запускаются самим эффектом.)

1
biziclop 20 Ноя 2019 в 02:40
Получает ли Dropdown изменяющуюся опору? Что-нибудь кроме fetchValues?
 – 
Alexander Staroselsky
20 Ноя 2019 в 02:44
Функция useEffect срабатывает только при изменении props.fetchValues. В опубликованном вами коде не похоже, что он меняется?
 – 
dwjohnston
20 Ноя 2019 в 02:45
На данный момент нет. Одна из возможностей - добавить "фальшивую" опору только для этого, но это кажется чем-то вроде взлома.
 – 
biziclop
20 Ноя 2019 в 02:56
В самом деле, именно поэтому этот эффект запускается только один раз. Теоретически я мог бы изменить идентификатор props.fetchValues, чтобы сигнализировать, что его нужно вызвать снова, но это звучит очень хрупко.
 – 
biziclop
20 Ноя 2019 в 02:58
1
Хорошо, так что либо передайте эту опору на несколько уровней и добавьте к ней массив зависимостей useEffect, используйте контекстный api для передачи параметров или этой опоры, либо используйте что-то вроде redux для получения параметров из магазина, который обновляется в компоненте, который наблюдает это изменение. Просто несколько возможных подходов.
 – 
Alexander Staroselsky
20 Ноя 2019 в 03:10

3 ответа

Последний параметр useEffect - это список зависимостей (при изменении любого значения внутри этого списка он снова вызовет useEffect), вы просто включаете [props.fetchValues], поэтому useEffect будет вызываться только при изменении fetchValues.

У вас есть 2 варианта:

  • передать другой props throw (управление состоянием, например, redux, context api, или реквизит кидаем родителям) и добавляем в список, вот так

    useEffect(() => {
     props.fetchValues().then(setOptions);
    }, [props.fetchValues, props.otherProps]);
    
  • оставьте зависимость нулевой (не пишите пустой список), и в этом указать, что он будет вызываться всякий раз, когда что-либо изменится в вашем приложении, например это

     useEffect(() => {
     props.fetchValues().then(setOptions);});
    
2
alaa tohamy 21 Ноя 2019 в 19:06
useEffect(() => {
      props.fetchValues().then(setOptions);
  }, [props.fetchValues, SOMETHING]);

Где SOMETHING - это то, что изменяется в вашем приложении и может вызывать выборку (лучше всего получить это значение из реквизита или контекста)

1
Maxwell s.c 20 Ноя 2019 в 15:22

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

Насколько я могу судить, причина, по которой моя первоначальная идея не сработала, заключается в том, что я нарушил принцип единой ответственности.

1
biziclop 20 Ноя 2019 в 15:14