我有这个组件类(和一些相关的方法):
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;通过传递以不同方式排序的相同数据(并使用onClick
在td
上使用id='"recent"
进行调用。但我不知道这是否真的是一个重新渲染的好模式。
如果点击td
并点击id="alltime"
,我还想重新退回原始数据。这部分似乎不起作用(每次按render
时都会调用td
方法,但没有任何变化)。我想我不能重新使用render
方法并希望rerender
它?
如果您遇到类似的情况,通常会采取什么样的模式?
更新
在codepen上发布我的原始代码,以便于调查:https://codepen.io/andriusl/pen/YxWXzg
答案 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>
);
}
}