【问题标题】:How to add new elements without re-rendering whole list with React如何在不使用 React 重新渲染整个列表的情况下添加新元素
【发布时间】:2016-12-29 04:27:33
【问题描述】:

我正在开发简单的流应用程序。我有帖子列表,这个列表可以接收更新,它会显示在上面。

问题是在每个新帖子上接收 React 都会重新呈现整个元素列表。我已经为它做了一个简单的例子。

有没有办法避免这种行为? 我在 React 文档上看到了 dynamic-children 主题,但例如,正如你所见,我已经更新了所有的孩子。

class Post extends React.Component {
  render() {
    console.log('rerendered post', this.props.reactKey);
    return (
      <li>{this.props.post.text}</li>
    );
  }
}

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {posts: [
      {id: '00001', text: 'First one'},
      {id: '00002',text: 'Second one'},
      {id: '00003',text: 'Third one'}
    ]};
  }

  addPost() {
    const posts = this.state.posts;
    posts.unshift({id: '00004', text: 'New post'});
    this.setState({posts: posts});
  }

  render() {
    return (
      <div>
        <button onClick={this.addPost.bind(this)}>Add Post</button>
        <ul>
          {this.state.posts.map((post, index) => {
            return (<Post post={post} key={post.id} reactKey={index} />);
          })}
        </ul>
      </div>
    );
  }
}


ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
  <div id="root"></div>
</body>

解决方案

问题是我使用 .map 函数的索引是每个列表组件的键而不是唯一键。而且因为在添加新元素以列出所有索引后更改为+1,所以第一个帖子变成了第二个,我所有的帖子都重新渲染了。 所以首先,检查你在所有列表元素中使用唯一键:-)

【问题讨论】:

  • 多次调用 render 并没有错,React 只会更新 DOM 的实际变化。在每个组件上调用 render 以确定是否有任何变化。如果您确定在您的组件中它甚至应该尝试调用渲染(我重申这很好),您可以使用shouldComponentUpdate
  • 问题是我不需要重新渲染旧帖子,因为有一些 API 调用等功能。我希望它们保持原样,只添加和渲染新帖子.
  • 那你必须使用shouldComponentUpdate来判断是否有相关的props发生了变化

标签: javascript dom reactjs


【解决方案1】:

只需要完成一次的工作应该在保证只运行一次的生命周期方法中完成,例如componentDidMount。正如文档所建议的那样:

如果您想与其他 JavaScript 框架集成,使用 setTimeout 或 setInterval 设置计时器,或发送 AJAX 请求,请在此方法中执行这些操作。

我在您的 sn-p 中将日志记录添加到 componentDidMount 以显示多次渲染,但每个实例仅调用一次 componentDidMount

class Post extends React.Component {
  componentDidMount() {
    console.log('mounted post', this.props.id);
  }

  render() {
    console.log('rerendered post', this.props.id);
    return (
      <li>{this.props.post.text}</li>
    );
  }
}

class App extends React.Component {

  constructor(props) {
    super(props);
    this.nextId = 4;
    this.state = {
      posts: [
        {id: 1, text: 'First one'},
        {id: 2,text: 'Second one'},
        {id: 3,text: 'Third one'},
      ],
    };
  }

  addPost() {
    const posts = this.state.posts;
    posts.unshift({id: this.nextId, text: 'Post ' + this.nextId});
    this.nextId++;
    this.setState({posts: posts});
  }

  render() {
    return (
      <div>
        <button onClick={this.addPost.bind(this)}>Add Post</button>
        <ul>
          {this.state.posts.map((post, index) => {
            return (<Post post={post} key={post.id} id={post.id} />);
          })}
        </ul>
      </div>
    );
  }
}


ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
  <div id="root"></div>
</body>

【讨论】:

  • 谢谢罗斯。实际问题是(因为我是 React 新手)我没有在所有列表元素中使用唯一键。我只使用了映射函数索引值,当然在添加新元素以列出我的所有元素之后,我的所有元素都重新渲染了,因为它们每个元素的键值都被改变了。
猜你喜欢
  • 2011-12-20
  • 2019-12-11
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-15
  • 1970-01-01
相关资源
最近更新 更多