处理从props传递过来的数据的最佳方法是什么?

时间:2018-12-15 11:32:13

标签: javascript reactjs

将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个事实来源。

解决此问题的最佳方法是什么?

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