Are state and props mutable? - reactjs

Let's say I have a react component - a button - that increments a value when I click it.
For example
var Component = React.createClass({
getInitialState: function() {
return {count: 0}
},
increment: function() {
this.setState({count: this.state.count + 1})
},
render: function() {
return (<button onClick={this.increment}>{this.state.count}</button>);
}
})
React.render(<Component />, document.getElementById('react-container'));
The state is mutable!
I can do a similar thing with props
var Component = React.createClass({
increment: function() {
this.setProps({count: this.props.count + 1})
},
render: function() {
return (<button onClick={this.increment}>{this.props.count}</button>);
}
})
React.render(<Component count={0}/>, document.getElementById('react-container'));
The state is mutable!
Some of the resources I have checkouted out say props are immutable, but then go and do something like setProps. The different resources keep contradicting each other. This is making it difficult for me to grasp the difference between state and props.
Are props mutable? If not why can I change them? It seems like changing props is not good practice, is this true? If yes, why does setProps exist?

this.props and this.state can be be mutated but this is really just a consequence of the mutability of objects in JS.
Your assertion that this.setState mutates the current state is false; a new object representing the state is created, with the changes applied.
To be honest I didn't even know that setProps existed but it sounds like a complete anti-pattern! The whole point of props is that they are passed through to the component, enforcing the unidirectional data flow. If the component can change its own props, this flow is broken.
You should really aim to store as much of your application state as possible at the top level i.e. in a store, rather than using component state. If all the state is in one place, it becomes a lot easier to reason about it.

setProps is a holdover from the early days of React, and it's been deprecated for a long time now. You're correct, changing props from within the component isn't a good practice - think of them as like the arguments to a function. Rather than mutating props, you should either:
Use a callback prop to notify the parent that something has happened - the parent can then mutate the data it owns and pass it back into the child through props.
Make use of component state as you did in your first example.
What both of these solutions have in common is that a single component owns the data, and that's the only component that's allowed to modify it - this is commonly referred to as there being a 'Single Source of Truth'. The great thing about structuring your code this way is that it means that you don't get stuck in tangles of spaghetti code trying to work who's mutating a piece of data.

Related

Is it ok to modify part of prevState inside setState?

Is there a real disadvantage to modifying part of prevState and returning that part inside setState() ?
Example:
this.setState(prevState => {
prevState.myObject.isItTrue = !prevState.myObject.isItTrue;
return {myObject: prevState.myObject};
});
Rather than:
this.setState(prevState => {
const myObject = Object.assign({}, prevState.myObject);
myObject.isItTrue = !myObject.isItTrue;
return {myObject: myObject};
});
Is there any real disadvantage to the first code where I save myself the Object.assign() ?
EDIT: If I am correct, prevState.myObject is simply a reference to this.state.myObject, so changing prevState.myObject actually changes this.myObject.object as well! However, this doesn't seem to break anything, as long as I use setState() to pass an object that contains the new data, even if it's just a reference to the old objects inside this.state.
Do you agree that this is still ok, i.e. it won't break anything to do it like this?
Following documentation:
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props.
https://reactjs.org/docs/react-component.html
So you should not apply changes directly to that state.
Either way, why not do something like this?:
this.setState(prevState => ({
myObject : {
...prevState.myObject,
isItTrue: !prevState.myObject.isItTrue,
}
}));
This way will get all the elements from the prevState but also change all the ones you want to modify.
First prevState and the this.state are the same object. So you are modifying the actual state directly.
Secondly, down the road you might pass the myObject as a prop to another component, and since it will be the same object always that component will not know that something has changed and it will not re-render (for example PureComponent and ones that implement componentDidUpdate or shouldComponentUpdate and test for changes)
See https://codesandbox.io/s/zen-aryabhata-m07l4 for showcases of all issues.
So you should use
this.setState(state => ({
myObject: {
...state.myObject,
isItTrue: !state.myObject.isItTrue
}
}));

Can I make a certain function globally available inside a React application?

It is a common practice to pass in the form of a prop, from a root component A, to a subcomponent B, a function that will change the state of A. Like so:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'foo'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
render() {
return (<NameChanger name={this.state.name} onNameChange={this.handleNameChange} />)
}
handleNameChange: function(newName) {
this.setState({
name: newName
});
}
}
Now as you can see NameChanger is one level down only so not a big issue there. But what if it had been down 3 or even 4 levels? We would have had to pass it down the chain of components and that bothers me big time. Is there a way to make a function globally available within the app?
I looked at Context (https://reactjs.org/docs/context.html) but I am not sure it is the right design choice for globally available functions. Or is it?
Thanks
In a typical React application, data is passed top-down (parent to
child) via props, but this can be cumbersome for certain types of
props (e.g. locale preference, UI theme) that are required by many
components within an application. Context provides a way to share
values like these between components without having to explicitly pass
a prop through every level of the tree.
https://reactjs.org/docs/context.html
Try using Redux or Mobx(very easy to start with) as state management library to solve this problem.

Immutability is an implementation detail in React?

I recently watched a talk by David Nolen where he says that 'immutability is an implementation detail in React'?
What does this mean and if this wasn't the case, how would React be different?
What does "implementation detail" mean:
I would summarize as:
Immutability is a detail of react that you have to implement yourself.
BTW: "Detail" is this case can still mean a lot of work.
React depends on props and state to be immutable.
React does not make props or state immutable for you. You have to ensure that in your code yourself.
So the following code is a recipe for disaster:
// DO NOT TRY THIS AT HOME
var customerObject = { name: "Bill" };
this.setState( customer: customerObject }; // valid react code, triggering re-render
...
customerObject.name = "Karl";
// state still has the same customerObject,
// but the contents of the object have changed. This is where things break down.
React has to ensure that its internal virtual DOM, and all props and states, are always in sync with the actual DOM.
So every time something changes anywhere in a prop or state, react needs to run its render cycle.
How would react be different without immutability:
Without immutability your react implementation may not work properly.
If react were not designed for immutability, then it would not be react (i.e. a state machine) but a different beast altogether.
Immutable Data Structure with ReactJS
The first of all, react team strongly recommend applying immutable data structure like Immutability Helpers or immutable.js. Why? Because we can use "shallow comparison" to increase component re-render performance. like
MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
}
render() {
...
}
}
According to immutability, the data alway return a new reference if it has been changed. We can easy use shallowEqual(only check reference whether is same or not) to determine component will re-render. If we dont use immutable data, we have to check props or state object deeply not reference to make sure re-rendering.
As for my understanding, each component in React has its own standalone scope and they don't share the variables.
That means when you pass an mutable variable(such as Object or Array) through props to a specific react component. It will clone each variable so that this component will have a totally new environment.
For example, assuming you have component A, and it works like this,
var ComponentA = React.createClass({
render: function() {
var user = { name: 'Tyler', role: 'Developer' };
return (
<SubComponent user={user} />
);
}
});
What ComponentA wants is simply render the user. So it require another module, let's say SubComponent to do that.
var SubComponent = React.createClass({
render: function() {
return (
<div>
<span>Name: {this.props.user.name}</span>
<span>Role: {this.props.user.role}</span>
</div>
);
}
});
For now, we should notice the variable user in ComponentA is different with the variable this.props.user in SubComponent. The this.props.user is not a reference. It's cloned from the ComponentA.
So that means, when you try to change the value of this.props.user in SubComponent, it won't destroy the user in ComponentA. Which is what David Nolen said in his tech talk. ("Change something in data without destroy the old one.")
Of course this would sacrifice some extra spaces, but you can get lots of benefits. Such as each of your component would be totally separated. Then all the nightmares cause by Shared Mutable Variables are gone. Shared Mutable Data is the root of evil, it's unpredictable and unreliable.
Imagine the SubComponent and the ComponentA are share the same user and you want to render another module by passing props user. Then you will update your code into this way,
var ComponentA = React.createClass({
render: function() {
var user = { name: 'Tyler', role: 'Developer' };
return (
<div>
<AnotherComponent user={user} />
<SubComponent user={user} />
</div>
);
}
});
Once we change the name of user in SubComponent(maybe by accident), we will have a cascading effect, and we don't know which one change the variable. That's painful coz then we have to check each line of the code in SubComponent and AnotherComponent. You really don't want to do that, right?
So I think that's what he mean. Hope this can solve your problem. : )

ReactJS: Should a large array of objects be passed down to many levels of child components as props?

I'm looking at one of my colleague's ReactJs code and noticed that he is passing an array of custom objects down to 5 levels of child components as props. He is doing this b/c the bottom level child component needs that array's count to perform some UI logic.
At first I was concerned about passing a potentially large array of objects down to this many levels of component hierarchy, just so the bottom one could use its count to do something. But then I was thinking: maybe this is not a big deal since the props array is probably passed by reference, instead of creating copies of this array.
But since I'm kind of new to React, I want to go ahead and ask this question here to make sure my assumptions are correct, and see if others have any thoughts/comments about passing props like this and any better approach.
In regards to the array being passed around I believe it is indeed a reference and there isn't any real downside to doing this from a performance perspective.
It would be better to make the length available on Child Context that way you don't have to manually pass the props through a bunch of components that don't necessarily care.
also it seems it would be more clear to pass only the length since the component doesn't care about the actual objects in the array.
So in the component that holds the array the 5th level child cares about:
var React = require('react');
var ChildWhoDoesntNeedProps = require('./firstChild');
var Parent = React.createClass({
childContextTypes: {
arrayLen: React.PropTypes.number
},
getChildContext: function () {
return {
arrayLen: this.state.theArray.length
};
},
render: function () {
return (
<div>
<h1>Hello World</h1>
<ChildWhoDoesntNeedProps />
</div>
);
}
});
module.exports = Parent;
And then in the 5th level child, who is itself a child of ChildWhoDoesntNeedProps
var React = require('react')
var ArrayLengthReader = React.createClass({
contextTypes: {
arrayLen: React.PropTypes.number.isRequired
},
render: function () {
return (
<div>
The array length is: {this.context.arrayLen}
</div>
);
}
});
module.exports = ArrayLengthReader;
I don't see any problems with passing a big array as a props, even the Facebook is doing that in one of their tutorial about Flux.
Since you're passing the data down to this many lever you should use react contex.
Context allows children component to request some data to arrive from
component that is located higher in the hierarchy.
You should read this article about The Context, this will help you with your problem.

Change state when properties change and first mount on React - Missing function?

I have come across a problem about states based on properties.
The scenario
I have a Component parent which creates passes a property to a child component.
The Child component reacts according to the property received.
In React the "only" proper way to change the state of a component is using the functions componentWillMount or componentDidMount and componentWillReceiveProps as far as I've seen (among others, but let's focus on these ones, because getInitialState is just executed once).
My problem/Question
If I receive a new property from the parent and I want to change the state, only the function componentWillReceiveProps will be executed and will allowed me to execute setState. Render does not allow to setStatus.
What if I want to set the state on the beginning and the time it receives a new property?
So I have to set it on getInitialState or componentWillMount/componentDidMount. Then you have to change the state depending on the properties using componentWillReceiveProps.
This is a problem when your state highly depends from your properties, which is almost always. Which can become silly because you have to repeat the states you want to update according to the new property.
My solution
I have created a new method that it's called on componentWillMount and on componentWillReceiveProps. I have not found any method been called after a property has been updated before render and also the first time the Component is mounted. Then there would not be a need to do this silly workaround.
Anyway, here the question: is not there any better option to update the state when a new property is received or changed?
/*...*/
/**
* To be called before mounted and before updating props
* #param props
*/
prepareComponentState: function (props) {
var usedProps = props || this.props;
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === usedProps.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
componentWillMount: function () {
this.prepareComponentState();
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
/*...*/
I feel a bit stupid, I guess I'm loosing something...
I guess there is another solution to solve this.
And yeah, I already know about this:
https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
I've found that this pattern is usually not very necessary. In the general case (not always), I've found that setting state based on changed properties is a bit of an anti-pattern; instead, simply derive the necessary local state at render time.
render: function() {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === this.props.currentQuestion.id;
});
return ...; // use currentResponses instead of this.state.currentResponses
}
However, in some cases, it can make sense to cache this data (e.g. maybe calculating it is prohibitively expensive), or you just need to know when the props are set/changed for some other reason. In that case, I would use basically the pattern you've written in your question.
If you really don't like typing it out, you could formalize this new method as a mixin. For example:
var PropsSetOrChangeMixin = {
componentWillMount: function() {
this.onPropsSetOrChange(this.props);
},
componentWillReceiveProps: function(nextProps) {
this.onPropsSetOrChange(nextProps);
}
};
React.createClass({
mixins: [PropsSetOrChangeMixin],
onPropsSetOrChange: function(props) {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
// ...
});
Of course, if you're using class-based React components, you'd need to find some alternative solution (e.g. inheritance, or custom JS mixins) since they don't get React-style mixins right now.
(For what it's worth, I think the code is much clearer using the explicit methods; I'd probably write it like this:)
componentWillMount: function () {
this.prepareComponentState(this.props);
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
prepareComponentState: function (props) {
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},

Resources