ReactJS - 用不同的数据重新渲染相同的组件?

时间:2017-08-01 16:20:03

标签: javascript reactjs rendering

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

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方法,我会在这里&#34;重新渲染&#34;通过传递以不同方式排序的相同数据(并使用onClicktd上使用id='"recent"进行调用。但我不知道这是否真的是一个重新渲染的好模式。

如果点击td并点击id="alltime",我还想重新退回原始数据。这部分似乎不起作用(每次按render时都会调用td方法,但没有任何变化)。我想我不能重新使用render方法并希望rerender它?

如果您遇到类似的情况,通常会采取什么样的模式?

更新

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

4 个答案:

答案 0 :(得分:2)

  

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

我认为不,如果你想通过使用状态变量来渲染具有不同数据管理的相同组件,请不要再次使用ReactDOM.render

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

  

语法onClick={() => this.render()}的问题:

根据 DOC

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

  

this.render()的问题:

调用render方法不是一个好主意,setState反应总是会自动重新渲染组件。

您可以编写如下代码:

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

答案 1 :(得分:1)

您应该只有一个主渲染方法。将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"));

答案 2 :(得分:0)

我有一些建议,首先.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,我发现该内容更易于阅读和维护。它还提供了一种更可重用的排序方法。

答案 3 :(得分:0)

如果进入组件的道具发生了变化,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>
        );
    }
}