Список обновлений неверно - PullRequest
1 голос
/ 27 марта 2019

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

Это как-то связано с запоминанием компонента Item, и я пытаюсь понять, что именно там происходит (valueChanged кэшируется?). Я не могу обернуться.

Без функции памятки код работает как положено, но, конечно, все входные данные обновляются при каждом изменении.

Вот гиф того, что происходит: https://streamable.com/gsgvi

Чтобы скопировать, вставьте приведенный ниже код в файл HTML или посмотрите здесь: https://codesandbox.io/s/81y3wnl142

<style>
  ul {
    list-style-type:none;
    padding:0;
  }

  input[type=text] {
    margin:0 10px;
  }
</style>


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

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script type="text/babel">

  const randomStr = () => Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
  const { useState, memo, Fragment } = React



  const Item = memo(({ value, i, valueChanged }) => {

    console.log('item rendering...');

    return <li>
      <input type='text' value={value} onChange={e => valueChanged(i, e.target.value)}/>
    </li>
  }, (o, n) => o.value === n.value)


  const ItemList = ({ items, valueChanged }) => {

    return <ul>{
      items.map(({ key, title }, i) => (
        <Item 
          key={key} 
          i={i}
          value={title}
          valueChanged={valueChanged}
        />
      ))
    }</ul>
  }



  const App = () => {

    const [items, setItems] = useState([
      { key: randomStr(), title: 'abc' },
      { key: randomStr(), title: 'def' },
    ])

    const valueChanged = (i, newVal) => {
      let updatedItems = [...items]
      updatedItems[i].title = newVal
      setItems(updatedItems)
    }

    const addNew = () => {
      let newItems = [...items]
      newItems.push({ key: randomStr(), title:'' })
      setItems(newItems)
    }

    return <Fragment>
      <ItemList items={items} valueChanged={valueChanged}/>
      <button onClick={addNew}>Add new</button>
    </Fragment>
  }


  ReactDOM.render(<App/>, document.querySelector('#app'))


</script>

Ответы [ 2 ]

4 голосов
/ 27 марта 2019

valueChanged - это закрытие, которое получает новый items каждый рендер.Но ваш memo не запускает обновление, потому что он проверяет только (o, n) => o.value === n.value.Обработчик событий внутри Item использует старое значение items.

Его можно исправить с помощью функциональных обновлений :

const valueChanged = (i, newVal) => {
  setItems(oldItems => {
    let updatedItems = [...oldItems];
    updatedItems[i].title = newVal;
    return updatedItems;
  });
}

Так что valueChanged не делает 't зависит от items и не нуждается в проверке memo.

Ваш код может иметь аналогичные проблемы с другими обработчиками.Функциональные обновления лучше использовать, когда новое значение основано на старом.

0 голосов
/ 27 марта 2019

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

https://codesandbox.io/s/q8l6m2lj64

    const [items, setItems] = useState(() => [
    { key: randomStr(), title: 'abc' },
    { key: randomStr(), title: 'def' },
]);
...