Список детей не обновляется правильно? (Реагировать / Preact) - PullRequest
1 голос
/ 19 марта 2020

У меня есть следующий компонент

import {h, Component} from 'preact'
import {getPersons} from '../../lib/datalayer'
import Person from '../person'
import {SearchInput} from '../search'

export default class Persons extends Component {
  state = {
    allPersons: [],
    persons: [],
    search: ''
  }

  async fetchData () {
    try {
      const allPersons = await getPersons()
      this.setState({allPersons: allPersons.slice(), persons: allPersons.slice()})
    } catch (error) {
      ....
    }
  }

  constructor (props) {
    super(props)
    this.state = {
      allPersons: [],
      persons: [],
      search: ''
    }
    this.fetchData()
  }

  onSearchInput = (search) => {
    if (search === '') {
      this.setState({search: search, persons: this.state.allPersons.slice()})
    } else {
      const persons = this.state.allPersons.filter(p => p.name.toLowerCase().includes(search.toLowerCase())).slice()
      this.setState({search: search, persons: persons)})
    }
  }

  render () {
    const {persons} = this.state
    return (
      <div>
        <SearchInput onInputChange={this.onSearchInput} placeHolder={'filter: name'} />
        {persons.map(p => <Person person={p} />)}
      </div>
    )
  }
}

На странице отображается список людей, а сверху находится фильтр. Кажется, что фильтр работает нормально, я проверил его, выполнив console.log с результатами просто отлично

Проблема в том, что, если мой список содержит объекты:

[{name: 'thomas'}, {name: 'john'}, {name: 'marcus'}, {name: 'usa'}]

И Я пишу в поиске: «us»

Фильтр работает отлично, и результат:

[{name: 'marcus'}, {name: 'usa'}] \\ (the expected result)

На странице эти объекты отображаются

[{name: 'thomas'}, {name: 'john'}] \\ (wrong, this are the two first elements of the list)

Если я ищу: 'joh'

Результат фильтра будет

[{name: 'john'}] \\ (this is fine)

И страница отображает только

[{name: 'thomas'}] \\ (the first element in the list)

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

Что не так с моим кодом?

Ответы [ 2 ]

3 голосов
/ 19 марта 2020

React использует keys для дочерних элементов списка, чтобы определить, какие элементы изменились, а какие из них остались прежними. Поскольку вы не указали key для человека, ключом будет index.

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

Исходя из вашего объекта, предполагая, что name уникален (обычно это не так):

 {persons.map(p => <Person person={p} key={p.name} />)}

Зачем нужны ключи - Документы

0 голосов
/ 19 марта 2020

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

const Person = React.memo(props => (
  {JSON.stringify(props, undefined, 2)}
)); класс Persons extends React.Component {state = {allPersons: [{name: 'aaa', id: 1}, {name: 'aab', id: 2}, {name: 'abb', id: 3}, { имя: 'bbb', id: 4}, {name: 'bb c', id: 5},], персон: [{name: 'aaa', id: 1}, {name: 'aab', id: 2}, {name: 'abb', id: 3}, {name: 'bbb', id: 4}, {name: 'bb c', id: 5},], поиск: '' ,}; onSearchInput = search => {if (search === '') {// срез здесь не нужен this.setState ({search: search, persons: this.state.allPersons,}); } else {// фильтр уже копирует все атрибуты allPersons person = this.state.allPersons.filter (p => p.name.toLowerCase (). includes (search.toLowerCase ())); this.setState ({поиск: поиск, персон: персоны}); }}; render () {const {people} = this.state; возврат (
this.onSearchInput (e.target.value)} placeHolder = {'filter: name'} /> {Person.map (p => ( ))}
); }} ReactDOM.render ( , document.getElementById ('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
...