Значение состояния по умолчанию не обновляется между визуализациями

#javascript #reactjs #react-hooks

#javascript #reactjs #реагирующие хуки

Вопрос:

Рассмотрим следующий пример. Я вызываю функцию выборки в родительском из дочернего.

Запрос завершается, я передаю данные из асинхронного запроса в мой Child as items prop. Я устанавливаю его как значение по умолчанию для useState React.useState(items); .

Ожидаемое поведение: запрос завершается, items обновляется, Child получает новые реквизиты, его повторный рендеринг и значение по умолчанию в useState обновляется. Таким a образом, переменная содержит соответствующий объект.

Фактическое поведение: значение по умолчанию в useState не обновляется между визуализациями. Почему?

 const Child = ({ fn, items }) => {
  const [a, b] = React.useState(items);
  console.log(a, items); // a is null but items is an object
  
  React.useEffect(() => {
    fn();
  }, []);

  return JSON.stringify(a);
}

const App = () => {
  const [stateOne, setStateOne] = React.useState(null);

  const fn = () => {
      fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(response => response.json())
      .then(json => {
        setStateOne(json);
      });
    }
    
  console.log(stateOne)
  return <Child fn={fn} items={stateOne} />;
}


ReactDOM.render(<App />, document.getElementById("root"));  
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>  

Комментарии:

1. fetch() является асинхронным. console() нет. Таким образом, fetch();console.log(fetchresults); , почти всегда пусто, если ваша сеть не FTL.

2. @HoldOffHunger Спасибо, но в этом примере это не так.

3. исправьте соглашение об именовании const [a, setA] = React.useState(items);

Ответ №1:

Значение по умолчанию намеренно используется только при монтировании компонента. Это то, что подразумевается под «по умолчанию». После этого единственный способ изменить состояние — вызвать функцию установки состояния ( b в вашем коде).

Редко бывает, что вам нужно копировать prop в state, поэтому вероятным решением является просто полное удаление состояния и использование prop.

 const Child = ({ fn, items }) => {
  React.useEffect(() => {
    fn();
  }, []);

  return JSON.stringify(items);
}
  

Если вам по какой-то причине необходимо иметь состояние, сначала подумайте, можете ли вы переместить это состояние до родительского и, таким образом, устранить эту проблему. Если по какой-то причине вы тоже не можете этого сделать, то вам потребуется, чтобы дочерняя логика реализации вызывала установщик состояния b , когда вы хотите, чтобы он был вызван. Например:

 const Child = ({ fn, items }) => {
  const [a, b] = React.useState(items);

  React.useEffect(() => {
    b(items);
  }, [items]);
  
  React.useEffect(() => {
    fn();
  }, []);

  return JSON.stringify(a);
}
  

Комментарии:

1. Итак, как только компонент смонтирован, он никогда не изменится, если только компонент не будет размонтирован и смонтирован снова, да?

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

Ответ №2:

Вы неправильно поняли, что значит иметь «значение по умолчанию» в случае реактивных перехватов. Это не столько «значение по умолчанию», сколько «начальное значение».

Рассмотрим следующий код:

 let n = Math.random();
const [myN, setMyN] = useState(n);

console.log(n) // This number will always be the same between renders
  

В вашем случае начальное значение равно null . Оно никогда не изменится, если только вы не вызовете для него установщик.

Если вы хотите передать prop, не смешивайте его с state .