From the React docs, the general consensus when it comes to Props vs State is as follows:

Props: (short for properties) are a Component’s configuration. They are received from above and immutable. State: starts with a default value when a Component mounts and then suffers from mutations in time. It is not advisable to store anything in a state that can be derived from props at any point in time

Learn more about React state management in 2019

Useful tip: Bit helps your team collaborate on shared components. Each component can be independently developed, tested and used across your apps. Bit handles the entire lifecycle of each shared component so you can focus on building great apps.

If you have been using React for sometime, you would have definitely come across use-cases where a component’s state has to mirror the props and mutate on its own, but keep listening to updates to the original prop as well.

Let us walk-through such a use-case with an illustrative example:

You see below a list of items that can be re-ordered by clicking on the drag icon to the left represented by the ItemList component. And it is possible to add new items to the list using the NewItem component.

The container component App houses these two components and co-ordinates data flow between them. First let’s define the ItemList component below:

ItemList.jsx

Notice line number 27: state = {items: this.props.items} where we copy the props into state. The reason to maintain the list of items in the state is to give the user quick feedback when he drags-and-drops an item, the change must be reflected in the component immediately. And that happens in onSortEnd component.

The basic set of actions is displayed in the recording below. It should be possible to re-order items in the list and add new items when the user enters one using the text box below.

App Actions and Behavior

The NewItem component is trivial and it is a typical fully controlled form component.

NewItem.jsx

When a new item is entered in the textfield, when the input element loses focus, NewItem component will call its parent, update the new value and clear the input element content.

Finally, the App component that orchestrates the above two components

App.js

App component is the ultimate source of truth and it maintains the list of items in its own state and passes it as props to the ItemList component. Now, when we add a new item to the app’s items state, the items prop of ItemList is changed, but its internal state remains unchanged, so the new item will not be rendered on the list.

Now, let’s discuss a couple of ways of mitigating this issue.

Move state up from the ItemList component to the App component

Since, App component is the owner of the list of items, it makes sense to not copy the props to state in ItemList component. When a drag-n-drop action occurs, we could move onSortEnd method from ItemList to App and do the re-ordering directly on the App. But, remember that in most real world applications, there will be a backend that is the real source of truth and you have to update the order in the backend. That means there is a redux action or a direct async action that will update the App again in response and we don’t want the UI feedback to be delayed till then!

2. Should we be thinking about componentWillReceiveProps ?

Now, that we are clear that ItemList needs to maintain the state of items, how should we update state in response to change to props. Earlier, we used to rely on the component lifecycle method called componentWillReceiveProps to update state based on changes to props. It is deprecated right now and for good reason. You can read more about it here.

3. Should we use getDerivedStateFromProps ?

The latest react guide recommends using this lifecycle method if state has to be derived from props provided you have a way of finding if something has changed in the props

The updated ItemList component will implement getDerivedStateFromProps method that checked the number of items between current and next props and overwrite state if needed. If getDerivedStateFromProps returns null, the state will not updated.

This solution will work for us both ways. If the items are re-ordered the state will be mutated. When a new item is added the internal state will be overwritten.

4. Fully uncontrolled component with a key

When the key changes, React will create a new component instance rather than update the current one. So, in our case, passing the total count of items as key (this is for the sake of this discussion, pls use more meaningful values for the key prop in real world apps) will overwrite the component instead of updating the existing one.

<ItemList items={this.state.items} key={this.state.items.length}/>

So when do you use choose between options 3 and 4. If the component is not that expensive to render and we can afford re-initializing the component when the props change, Option 4 is recommended. Else, use getDerivedStateFromProps , but with caution. Remember that shoddy use of this lifecycle method can cause the component to be rendered indefinitely or have weird side-effects as this gets invoked when both props and state change!

Note: In our example, when a new item is added, the changed order in ItemList component is lost and the list is rendered in the original order, since we are not updating the order change to a backend. This has been skipped for the sake of brevity of this post.

Conclusion

The final working version of the code is available in the sandbox here for your reference. Thanks for reading, and please feel free to comment, ask anything and add your own insights! Cheers 👏