【问题标题】:List childs are not beeing updated correctly? (React / Preact)未正确更新列表子项? (反应/预演)
【发布时间】:2020-03-19 12:45:37
【问题描述】:

我有以下组件

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>
    )
  }
}

页面呈现人员列表,顶部有一个过滤器。过滤器似乎工作正常,我通过控制台进行了测试。结果很好

问题是,如果我的列表包含对象:

[{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)

看起来渲染的元素数量很好,但是列表的子元素的内容没有被重新渲染。

我的代码有什么问题?

【问题讨论】:

    标签: javascript reactjs preact


    【解决方案1】:

    React 在列表的子项上使用keys 来确定哪些项目发生了变化,哪些项目保持不变。由于您没有在 person 上指定 key,因此需要 index 作为键。

    当索引是关键时,您可以看到如何将列表缩短为两个项目,从而显示列表中的前两个项目(其他索引现在丢失了)。为了解决这个问题,你必须给这个人一个唯一的标识符作为键。

    根据您的对象,假设 name 是唯一的(通常不是):

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

    Why are keys necessary - Docs

    【讨论】:

      【解决方案2】:

      我无法通过 react 重现错误,确实删除了一些不需要的切片并为每个元素添加了唯一的 id(如果你不给每个元素一个唯一的键,React 会抱怨,也许会这样)。

      const Person = React.memo(props => (
        <pre>{JSON.stringify(props, undefined, 2)}</pre>
      ));
      
      class Persons extends React.Component {
        state = {
          allPersons: [
            { name: 'aaa', id: 1 },
            { name: 'aab', id: 2 },
            { name: 'abb', id: 3 },
            { name: 'bbb', id: 4 },
            { name: 'bbc', id: 5 },
          ],
          persons: [
            { name: 'aaa', id: 1 },
            { name: 'aab', id: 2 },
            { name: 'abb', id: 3 },
            { name: 'bbb', id: 4 },
            { name: 'bbc', id: 5 },
          ],
          search: '',
        };
      
        onSearchInput = search => {
          if (search === '') {
            //slice not needed here
            this.setState({
              search: search,
              persons: this.state.allPersons,
            });
          } else {
            //filter already copies allPersons
            const persons = this.state.allPersons.filter(p =>
              p.name.toLowerCase().includes(search.toLowerCase())
            );
            this.setState({ search: search, persons: persons });
          }
        };
      
        render() {
          const { persons } = this.state;
          return (
            <div>
              <input
                type="text"
                value={this.state.search}
                onChange={e => this.onSearchInput(e.target.value)}
                placeHolder={'filter: name'}
              />
              {persons.map(p => (
                <Person person={p} key={p.id} />
              ))}
            </div>
          );
        }
      }
      
      ReactDOM.render(
        <Persons />,
        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>

      【讨论】:

        猜你喜欢
        • 2021-10-05
        • 1970-01-01
        • 1970-01-01
        • 2018-09-09
        • 1970-01-01
        • 1970-01-01
        • 2016-02-24
        • 2021-07-03
        • 2019-06-07
        相关资源
        最近更新 更多