将prop分配到组件状态被认为是一种不好的做法。但是有时这似乎是必要的,以便您可以更改该状态并重新渲染组件。
当前,我的数据源是从props传递过来的,然后我想将其分配给构造函数中的state,使其默认。然后,当单击一个按钮时,我想对数据进行排序/过滤,然后设置状态并重新渲染组件。
class UsersList extends Component {
constructor(props){
super(props)
this.state = {
users: props.users
}
}
filterByLastName = (lastName) => {
const { users } = this.state
const filtered = users.map(u => u.lastName === lastName)
this.setState({ users: filtered })
}
render(){
const { users } = this.state
return(
<>
<button onClick={this.filterByLastName("Smith")}>Filter</button>
<ul>
{
users.map(u => (
<>
<li>{u.firstName}</li>
<li>{u.lastName}</li>
</>
)
)}
</ul>
</>
)
}
}
这样做的问题是,如果组件属性发生更改,即this.props.users
,它将导致重新渲染,但不会调用构造函数,因此该组件仍将为其分配了旧的用户列表{{1 }}值。您可以使用props.users
(或其他生命周期方法),但这看起来很杂乱且过于复杂。
如果您的数据来自道具,那么将其分配给状态似乎是一种不好的做法,因为最终您有2个事实来源。
解决此问题的最佳方法是什么?
答案 0 :(得分:2)
这就是getDerivedStateFromProps
的作用。它会在每次组件更新时计算派生状态。
有必要将所有用户与过滤后的用户分开,因为过滤后的更改需要重置过滤后的用户列表。
const filterByLastName = (users, lastName) => users.map(u => u.lastName === lastName);
class UsersList extends Component {
static getDerivedStateFromProps(props, state) {
return {
users: state.filter ? props.users : filterByLastName(props.users, state.filter)
}
}
render(){
const { users } = this.state
return(
<>
<button onClick={() => this.setState({ filter: "Smith" })}>Filter</button>
<ul>
{
users.map(u => (
<>
<li>{u.firstName}</li>
<li>{u.lastName}</li>
</>
)
)}
</ul>
</>
)
}
}
答案 1 :(得分:1)
触发渲染的来源是某种“激活过滤器”操作,对吗?我会将filterByLastName
逻辑移到render方法的开头,然后在状态下放置一个nameFilter
var,并改为设置filterByLastName
方法。
这样,您可以检查nameFilter
状态变量以在渲染时对传入的道具应用过滤,因此渲染仅在更改过滤器时发生,并且您保留了一个真实的来源而无需保存道具进入状态。
未经测试,但我想可能是这样的:
class UsersList extends Component {
state = {
nameFilter: null
}
filterByLastName = lastName => this.setState({ nameFilter: lastName })
render(){
const { users } = this.props
const { nameFilter } = this.state
const filtered = nameFilter ? users.filter(u => u.lastName === nameFilter) : users
return(
<>
<button onClick={() => this.filterByLastName("Smith")}>Filter</button>
<ul>
{
filtered.map(u => (
<>
<li>{u.firstName}</li>
<li>{u.lastName}</li>
</>
)
)}
</ul>
</>
)
}
}