Крюк государство один позади пропел государства - PullRequest
0 голосов
/ 07 ноября 2019

У меня возникла проблема с тем, как реквизиты взаимодействуют с состоянием, создаваемым хуками.

В приведенном ниже примере состояние, удерживаемое в хуке, всегда одно за этим, как определено в реквизитах.

Я думаю Я понимаю, почему это происходит. Жизненный цикл выглядит примерно так:

  1. Компонент рендеринга
  2. handleClick определяется с доступом к области действия на основе этого рендера
  3. Нажатие кнопки извонки handleClick
  4. handleClick по-прежнему имеют доступ только к той области, которая была определена в рендере, и поэтому items по-прежнему пуст
  5. add вызывается;состояние в родительском компоненте обновляется, но setHookItems будет использовать состояние, которое было определено в подпорках, когда handleClick было определено
  6. hookItems получает значение items, которое пусто

Это для первого рендера, но я думаю, что принцип применим ко всем остальным;обработчик щелчков имеет доступ только к области действия до добавления нового элемента и поэтому устанавливает состояние подключения на основе того, что он имеет в качестве состояния для items.

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

Итак, к фактическому вопросу;Как я могу обновить элемент в состоянии ловушки, основываясь на последних реквизитах, из какого-то рода обработчиков, а не на реквизитах во время последнего рендеринга?

const { useState } = React;

const App = ({items, add}) => {
  const [hookItems, setHookItems] = React.useState([]);

  const handleClick = () => {
    add(Math.floor(Math.random() * 10));
    setHookItems(items);
  }


  return (
    <div>
      <p><span class="title">All Items: </span>{items.map(x => (<span>{x}</span>))}</p>
      <p><span class="title">Hook Items: </span>{hookItems.map(x => (<span>{x}</span>))}</p>
      <button onClick={handleClick}>Add</button>
    </div>
  );
}



const Root = () => {
  const [items, setItems] = useState([]);
  return (
    <div>
      <App items={items} add={item => setItems([...items, item])} />
    </div>
  )
}


ReactDOM.render(
  <Root />,
  document.getElementById('app')
);
.title { display: inline-block; width: 100px; text-align: right; margin-right: 20px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

<div id="app"></div>

1 Ответ

1 голос
/ 07 ноября 2019

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

Так что, если у меня есть состояние вроде:

const [count, setCount] = useState(0);

Если я сделаю setCount(count + 1), он поставит в очередь новый рендер, где переменная count будет 1, но это не так. не заставляет переменную count сразу становиться 1. (Это не может - const переменные не могут быть изменены) Так что если бы я зарегистрировал count сразу после установки, он все равно был бы равен нулю.

В вашем примере естьуровень косвенности, где установщик находится в родительском компоненте, запускаемый функцией add, и значение передается потомку как опора, но это не меняет поведения.


С точки зрения дизайна, проблема здесь в том, что вы используете шаблон "props to state", который обычно является анти-шаблоном. Ваши компоненты, как правило, не должны сохранять реквизиты как состояние, вы должны использовать реквизиты напрямую. В противном случае вы столкнетесь с проблемами, когда реквизиты и состояние не синхронизированы.

В блоге реагирования есть статья Возможно, вам не нужно производное состояние , в которой это рассматривается более подробно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...