【问题标题】:ReactJS - rerender same component with different data?ReactJS - 用不同的数据重新渲染相同的组件?
【发布时间】:2017-08-01 16:20:03
【问题描述】:

我有这个组件类(以及一些相关的方法):

const sortData = (name, data) => {
    return data.sort((a, b) => {return b[name] - a[name]});
};

class LeaderTable extends React.Component {
    renderByRecent() {
        let data = sortData('recent', this.props.list);
        ReactDOM.render(
            <LeaderTable list={data}/>,
            document.getElementById('board'))
    }
    render() {
        return (
            <table className="table table-bordered">
                <thead>
                    <tr>
                        <td><strong>#</strong></td>
                        <td><strong>Camper Name</strong></td>
                        <td id="recent" onClick={() => this.renderByRecent()} className="text-center">Points in past 30 days</td>
                        <td id="alltime" onClick={() => this.render()} className="text-center">All time points</td>
                    </tr>
                </thead>
                <tbody>
                    {this.props.list.map(function(item, index) {
                        let url = "https://www.freecodecamp.com/" + item.username;
                        return (
                            <tr key={index}>
                                <td>{index}</td>
                                <td>
                                    <a href={url}>
                                        <img src={item.img} className="logo"/> {item.username}
                                    </a>
                                </td>
                                <td className="text-center">{item.recent}</td>
                                <td className="text-center">{item.alltime}</td>
                            </tr>
                        );
                    })}
                </tbody>
            </table>
        );
    }
}

现在第一次渲染发生在页面加载时。它只是在 javascript 文件中调用,如下所示:

ReactDOM.render(
    <LeaderTable list={campersData}/>,
    document.getElementById('board'))

这个很好用。

我现在需要的是重新渲染相同的组件,但使用不同的数据(实际上只是使用不同的数据顺序)。

如果您查看renderByRecent 方法,在这里我通过传递以不同方式排序的相同数据来“重新渲染”(并且在tdid='"recent" 上使用onClick 调用。但我不知道这是否确实是一个很好的重新渲染模式。

如果单击tdid="alltime",我还想重新渲染原始数据。这部分似乎不起作用(每次我按下相应的td 时,它都会调用render 方法,但没有任何变化)。我想我无法调用render 方法并希望rerender 它?

如果你有类似这样的情况,react通常会做什么样的模式?

更新

将我的原始代码发布在 codepen 上,以便于调查:https://codepen.io/andriusl/pen/YxWXzg

【问题讨论】:

    标签: javascript reactjs rendering


    【解决方案1】:

    真的是重新渲染的好模式吗?

    我认为不,如果您想用不同的数据渲染相同的组件,请使用状态变量进行管理,不要再次使用ReactDOM.render

    要么使用状态变量来保存您想要对数据进行排序的键名,然后在创建 UI 期间检查并排序数据,或者您可以将道具数据存储在状态变量中并修改该数据。

    语法问题onClick={() =&gt; this.render()}:

    根据DOC

    这种语法的问题是每次渲染组件时都会创建不同的回调,所以最好在构造函数中绑定方法。

    this.render() 的问题:

    调用render方法不是个好主意,总是做setStatereact会自动重新渲染组件。

    你可以这样写代码:

    const sortData = (name, data) => {
        return data.sort((a, b) =>  b[name] - a[name]);
    };
    
    class LeaderTable extends React.Component {
        constructor(){
            super();
            this.state = {
                sortBy: ''
            }
        }
    
        _renderList(){
            let data = this.props.list.slice(0);
    
            if(this.state.sortBy){
                data = sortData(this.state.sortBy, data);
            }
    
            return data.map(function(item, index) {
                let url = "https://www.freecodecamp.com/" + item.username;
                return (
                    <tr key={index}>
                        <td>{index}</td>
                        <td>
                            <a href={url}>
                                <img src={item.img} className="logo"/> {item.username}
                            </a>
                        </td>
                        <td className="text-center">{item.recent}</td>
                        <td className="text-center">{item.alltime}</td>
                    </tr>
                );
            });
        }
    
        renderByRecent() {
            this.setState({
                sortBy: 'recent'
            });
        }
    
        renderOriginalList(){
           this.setState({
                sortBy: ''
           });
        }
    
        render() {
            return (
                <table className="table table-bordered">
                    <thead>
                        <tr>
                            <td><strong>#</strong></td>
                            <td><strong>Camper Name</strong></td>
                            <td id="recent" onClick={() => this.renderByRecent()} className="text-center">Points in past 30 days</td>
                            <td id="alltime" onClick={() => this.renderOriginalList()} className="text-center">All time points</td>
                        </tr>
                    </thead>
                    <tbody>
                        {this._renderList()}
                    </tbody>
                </table>
            );
        }
    }
    

    【讨论】:

    • 请提供不赞成票的原因,我会更正我的答案:)
    • 是的,我正在写它。 Issue with syntax onClick={() =&gt; this.render()} - 绑定是该语法中最少的错误。性能方面 - 如果您没有真正大规模的应用程序,您将不会注意到它。该代码的主要问题是在反应中调用this.render 是重新渲染组件的糟糕方式 - 主要是因为状态和道具不会被更新。如果您确实需要重新渲染组件,您将使用this.forceUpdate() SO 上的链接 - stackoverflow.com/questions/30626030/…
    • this.sortData 不能使用,因为sortData 是外部方法。所以它应该只是sortDataalltime 部分也不起作用,因为你和我一样使用相同的 this.render,这似乎没有任何用处。
    • @Andrius 这是一个小错误,从答案中删除了this,对不起,我没有检查那个市场,让我知道this.render 的使用也会告诉你解决方案:)
    • @Andrius 只需调用一个函数,在其中将状态值恢复为''(空白),每当我们执行 setstate 时,它​​将使用更新的数据重新渲染组件。它会工作,检查更新的答案:)
    【解决方案2】:

    您应该只有一种主要的渲染方法。将 React 组件挂载到组件外部的 DOM 上,并让 React 在组件状态更改时管理控制 DOM 更新。

    更正引脚codepen

    class LeaderTable extends React.Component {
        constructor() {
            super();
    
            this.state = {
                sort: false
            };
    
            this.renderByRecent = this.renderByRecent.bind(this); // bind this context to method
        }
    
        renderByRecent() {
            let data = this.props.list.slice();
    
            if (this.state.sort) {
                data.sort((a, b) => {
                    return b.recent - a.recent;
                });
            }
    
    
    
            return data.map(function(item, index) {
                let url = "https://www.freecodecamp.com/" + item.username;
                return (
                    <tr key={index}>
                        <td>
                            {index}
                        </td>
                        <td>
                            <a href={url}>
                                <img src={item.img} className="logo" />{" "}
                                {item.username}
                            </a>
                        </td>
                        <td className="text-center">
                            {item.recent}
                        </td>
                        <td className="text-center">
                            {item.alltime}
                        </td>
                    </tr>
                );
            });
        }
    
        render() {
            return (
                <table className="table table-bordered">
                    <thead>
                        <tr>
                            <td>
                                <strong>#</strong>
                            </td>
                            <td>
                                <strong>Camper Name</strong>
                            </td>
                            <td
                                id="recent"
                                onClick={() => this.setState({ sort: true })}
                                className="text-center"
                            >
                                Points in past 30 days
                            </td>
                            <td
                                id="alltime"
                                onClick={() => this.setState({ sort: false })}
                                className="text-center"
                            >
                                All time points
                            </td>
                        </tr>
                    </thead>
                    <tbody>
                        {this.renderByRecent()}
                    </tbody>
                </table>
            );
        }
    }
    
    ReactDOM.render(<LeaderTable list={data} />, document.getElementById("board"));
    

    【讨论】:

    • 试过这个例子,但结果似乎和我的一样。出于某种原因,当我按下td='alltime' 时它不会呈现回来。我的意思是什么都不会发生。也许你知道为什么?
    • 您可以在 render post 方法中console.log(data, this.state.sort) 并确保在相应的点击事件上更新状态以及验证数据是否正确,我同意其他一些您应该考虑使用 Array.prototype.sort 方法进行排序与​​滚动您自己的功能的帖子 -> developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    • 嗯,如果我在调用render 时按alltime td,我确实看到sortfalse。但它仍然不会回到以前的状态(我的意思是它加载时的状态)。
    • 你是在 Codepen 中为 freecodecamp 模拟这个吗?如果是这样,你能给我发个链接让我玩吗?
    • 是的,我愿意。我实际上只是在一个问题中添加了链接。查看Update 部分。
    【解决方案3】:

    我有一些建议,首先.sort() 工作in-place 意味着你正在改变你试图排序的数据。出于这个原因,我更喜欢先做一个.slice(),然后再做一个.sort()。这将返回一个根据您传入的任何排序函数排序的新数组,不改变数据是一种很好的函数式编程实践。关于一个好的方法,它取决于你如何管理你的状态,但通常需要强制更新(根据我的经验)表明你应该重新考虑你的逻辑,我只会强制更新作为最后的手段。

    如果您通常在组件内管理您的状态,我会将数据保存到状态变量中,然后使用不同的方法根据我的需要对数据进行排序。举个简单的例子:

    ...
    constructor(props) {
      super(props);
      this.state = { listData: this.props.list };
    
      this.sortBy = this.sortBy.bind(this);
    }
    
    sortBy(field) {
      const { listData } = this.state;
      const sortedList = listData
        .slice()
        .sort((a,b) => b[field] - a[field]); 
    
      this.setState({ listData: sortedList });
    }
    
    
    render() {
      return (
        <div>
          {this.state.listData.map(listItem => <MyComponent ...listItem />)}
          <button onClick={() => this.sortBy('recent')}>Sort by recent</button>
          <button onClick={() => this.sortBy('alltime)}>Sort by all time</button>
        </div>
      )
    }
    ...
    

    编辑

    虽然您已经接受了答案,但请查看此implementation,我发现它更易于阅读和维护。它还提供了一种更可重用的排序方法。

    【讨论】:

      【解决方案4】:

      如果进入组件的 props 发生变化,React 将自动重新渲染。所以排序应该发生在将道具传递给这个组件的更高级别的组件上。你也不应该像那样改变道具。

      您也可以让这个组件以自己的状态管理列表,如下所示:

      const sortData = (name, data) => {
          return data.sort((a, b) => {return b[name] - a[name]});
      };
      
      class LeaderTable extends React.Component {
          constructor(props) {
              super(props)
              this.state={
                  list: [{ name: 'Jerry'}, {name: 'Tabitha'}]
              }
          }
          renderByRecent() {
              // spread the array so it doesn't mutate state
              this.setState({
                 list: sortData('name', [...this.state.list])
              })
          }
          render() {
              return (
                  <table className="table table-bordered">
                      <thead>
                          <tr>
                              <td><strong>#</strong></td>
                              <td><strong>Camper Name</strong></td>
                              <td id="recent" onClick={() => this.renderByRecent()} className="text-center">Points in past 30 days</td>
                              <td id="alltime" onClick={() => this.render()} className="text-center">All time points</td>
                          </tr>
                      </thead>
                      <tbody>
                          {this.state.list.map(function(item, index) {
                              let url = "https://www.freecodecamp.com/" + item.username;
                              return (
                                  <tr key={index}>
                                      <td>{index}</td>
                                      <td>
                                          <a href={url}>
                                              <img src={item.img} className="logo"/> {item.username}
                                          </a>
                                      </td>
                                      <td className="text-center">{item.recent}</td>
                                      <td className="text-center">{item.alltime}</td>
                                  </tr>
                              );
                          })}
                      </tbody>
                  </table>
              );
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-05-17
        • 2019-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-01
        • 2023-04-11
        • 2020-06-20
        相关资源
        最近更新 更多