6
votes

I'm having the following class that renders users based on a sort dropdown. Users will be listed in alphabetical order if i choose "alphabetical" and in group order when i choose "group".

render(){
    return(
        const {members, sort} = this.state
        { sort === "alphabetical" && <SortByAlphabet members={members} /> }
        { sort === "group" && <SortByGroup members={members}/> }
    )
)

In <SortByAlphabet /> component I am setting a component state object from props.members in componentWillReceiveProps() function.

componentWillReceiveProps = props => {
    this.setState({ members : props.members });
}

When I choose "group" sort, <SortByAlphabet /> component is unmounting and <SortByGroup /> is mounting in the DOM. Again when i switch back to "alphabetical" sort, the state variable (members) that was set previosly in <SortByAlphabet /> component becomes NULL because the component was removed from the DOM.

componentWillReceiveProps function is not triggering the second time when re-rendering <SortByAlphabet /> b'coz the props didn't change. But i want to update the state variable like i did it for the first time in componentWillReceiveProps function.

How to do that?

2
Using different components to render sorting order is not a good choice. From the doc: "Two elements of different types will produce different trees.". So even though all your children elements are the same (just ordered diffently), they will be destroyed and recreated instead of just being reordered. See reactjs.org/docs/reconciliation.html for detailed explaination.cquezel

2 Answers

6
votes

componentWillMount is called only once in the component lifecycle, immediately before the component is rendered. It is usually used to perform any state changes needed before the initial render, because calling this.setState in this method will not trigger an additional render So you can update your staate using

componentWillMount ()
{

        this.setState({ members : props.members });


}
6
votes

As @Vikram also said, componentWillReceiveProps is not called for the first time, so when your component is initially mounted your state is not getting set, so you need to set the state with props in the componentWillMount/componentDidMount function(which are called only on the first render) also along with the componentWillReceiveProps function

componentWillReceiveProps = props => {
    if(props.members !== this.props.members) {
        this.setState({ members : props.members });
    }
}

componentWillMount() {
     this.setState({ members : this.props.members });
}

From version 16.3.0 onwards, you would make use of getDerivedStateFromProps method to update the state in response to props change,

getDerivedStateFromProps is invoked after a component is instantiated as well as when it receives new props. It should return an object to update state, or null to indicate that the new props do not require any state updates.

static getDerivedStateFromProps(nextProps, prevState) {
    if(nextProps.members !== prevState.memebers) {
         return { members: nextProps.members };
    }
    return null;
}

EDIT: There has been a change in getDerivedStateFromProps API from v16.4 where it receives props, state as arguments and is called on every update along with initial render. In such a case, you can either trigger a new mount of the component by changing the key

<SortByAlphabet  key={members} />

and in SortByAlphabet have

componentWillMount() {
     this.setState({ members : this.props.members });
}

or use getDerivedStateFromProps like

static getDerivedStateFromProps(props, state) {
    if(state.prevMembers !== props.members) {
         return { members: nextProps.members, prevMembers: props.members };
    }
    return { prevMembers: props.members };
}