What is the difference between React component instance property and state property? - javascript

Consider the below example
class MyApp extends Component {
counter = 0;
state = {
counter: 0
};
incrementCounter() {
this.counter = this.counter + 1;
this.setState({
counter: this.state.counter + 1
});
}
render() {
return <div>
<p>{this.counter} and {this.state.counter}</p>
<button onClick={this.incrementCounter}>Increment</button>
</div>
}
}
When I click the button I see both this.counter and this.state.counter are showing the incremented value
My question is why I have to use state? though react is capable of re-rendering all the instance properties
counter = 0;
incrementCounter() {
this.counter = this.counter + 1;
this.setState({});
}
In above snippet, just calling this.setState({}) is doing the trick, then why should I use this.state property for storing my component state?

state and instance properties serve different purposes. While calling setState with empty arguments will cause a render and will reflect the updated instance properties, state can be used for many more features like
comparing prevState and currentState in shouldComponentUpdate to decide whether you want to render or not, or in lifecycle method like componentDidUpdate where you can take an action based on state change.
state is a special instance property used by react to serve special purposes. Also in setState, state updates are batched for performance reasons and state updates happen asynchronously unlike class variable updates which happen synchronously. A class variable won't have these features.
Also when you supply a class variable as prop to the component, a change in this class variable can't be differentiated in the lifecycle methods of the child component unless you are creating a new instance of the variable yourself. React does it with state property for you already.

Both have different purpose. Rule of thumb is:
Use state to store data if it is involved in rendering or data flow (i.e. if its used directly or indirectly in render method)
Use other instance fields to store data if value is NOT involved in rendering or data flow (to prevent rendering on change of data) e.g. to store a timer ID that is not used in render method. See TimerID example in official docs to understand this valid case.
If some value isn’t used for rendering or data flow (for example, a timer ID), you don’t have
to put it in the state. Such values can be defined as fields on the
component instance.
Reference: https://reactjs.org/docs/react-component.html#state

Related

Why I am getting the old state values after the props change

I just want to understand why in the application I have following situation, below is my constructor of class component:
constructor(props) {
super(props);
this.state = {
tableAlerts: props.householdAlerts,
initialAlerts: props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}
in render function I have:
const { householdAlerts } = this.props;
My issue is that in constructor I got empty array, but in render funtion I have the data. Is it possible to get the data in constructor?
This is a very bad pattern when using the class component. You are ignoring any props updates when you copy the value into state. to manage it:
It requires you to manage two sources of data for the same variable: state and props. Thus, you need to add another render each time your prop change by setting it into state (don't forget to test on equality from prev and next values to avoid being in an infinite loop).
You can avoid setting the state each time your props change by using the getderivedstatefromprops lifecycle method.
So the recommendation is: just use the props; do not copy props into state.
To learn more why you shouldn't, I highly recommend this article.
It is not recommended to set your initial component state in the constructor like so because you gonna lose the ability to use { setState } method after to update this property/state.
The best practice is indeed to refer directly to the prop with { this.prop.householdAlerts }, and keep the state usage for local (or in child components} cases.
if anyhow you want to store props in component state for some reason, call it in lifeCycle -
componentDidMount() {
const { tableAlerts, initialAlerts } = this.props;
this.setState({ tableAlerts, initialAlerts });
}
Hagai Harari is right. Nevertheless, your actual problem seems to be that during your initial rendering the array is empty. Can you ensure that the array has some items, when your component is rendered for the first time?
First rendering -> calls constructor
<YourComponent householdAlerts={[]} />
Second rendering -> updates component
<YourComponent householdAlerts={[alert1, alert2, alert3]} />
If you want initial state to have the prop value.Try something like this with 'this' keyword
constructor(props) {
super(props);
this.state = {
tableAlerts: this.props.householdAlerts,
initialAlerts: this.props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}

Is there a problem with setting React.Component state equal to a this.props value in typescript?

I am attempting to assign an object that is passed into a React.Component as a property to a state value like so,
state = {
rota: this.props.rota
}
render() {
// const { cleaning, chairs, creche, flowers } = this.state.rota;
const { cleaning, chairs, creche, flowers } = this.props.rota;
console.log(creche);
}
The commented out section where it get's the value of the state prints out an empty string, however the property values are correct. Am I doing something wrong when assigning the props.rota to the state.rota?
I am using typescript and have done exactly this in another place in my program - however the property being passed in was a value type (string) rather than an object type.
Thanks!
That's usually a bad practice.
Take your case.
You get a value as prop from a parent component.
The inner component will already be re rendered every time that this value changes in the parent component.
If you go with an approach like yours, what most probably you will do, is to change that value inside the inner component too (through state), whose changes will not be reflected on the parent component.
You are actually breaking the design pattern of uni directional data flow, on which react relies a lot on.
So my personal opinion is to lift up the state in this case and avoid such kind of situations. Use callbacks instead if you want to communicate changes to the parent, or use some state management (context, redux, etc..).
Or design a better solution using HOC or render props Components.
Even though #quirimmo has pretty much answered your question, if you want to do this sometime in the future, the easiest way would be to use a constructor function and pass the props in as a param, and then just set that as the default value of the state
class SomeComponent extends Component {
constructor(props){
super(props);
this.state = {
rota: props.rota,
}
}
}
This makes sure that the prop is actually available in the moment you want to set the initial state, since the constructor is the first function that is called in the component lifecycle.

Is React's setState() guaranteed to create a new state object every time?

In React, does setState always assign a new object to this.state?
In other words, when you call:
this.setState({
key1: val1,
key2: val2
});
does it always merge the current state object with the new properties into a new object, queueing an operation which is functionally equivalent to the following?
this.state = {
...this.state,
key1: val1,
key2: val2
};
The reason I'm asking is that I'd like to know whether this.state !== nextState is always guaranteed to be true inside shouldComponentUpdate() if the update was triggered by setState.
Thanks.
The simple answer to your question will be "Yes", but for one update cycle. Let me explain this.
As you may already know React setState does not always immediately update the component. Sometimes React batch or defer the updates until later. As an example, let's assume you have something like follows in your event handler.
onClick() {
this.setState({quantity: state.quantity + 1});
this.setState({quantity: state.quantity + 1});
this.setState({quantity: state.quantity + 1});
}
These state update will be queued and execute all together in one update cycle. In such scenario React only create a new state object for the first setState and that object will mutate for subsequent setState updates. You can clearly see this in the source code here.
However, this is something totally about how React works under the hood and we won't need to worry about that. Because there will be only one shouldComponentUpdate() and componentDidUpdate() for one cycle. By the time we access the state it will be always a new object. So we can safely assume that setState() guaranteed to create a new state object every time. But make sure that you aware of other implications of setState which explains in the official documentation.
I'd like to know whether this.state !== nextState is always guaranteed
to be true inside shouldComponentUpdate().
The answer to your this question will be "No" as also explained in other answers. shouldComponentUpdate() will be called because of either state change, props change or both. So this.state !== nextState won't true if the component has updated only because of props change.
I think you want to know the answer to this question.
Will this.state !== nextState is always guaranteed to be true inside
shouldComponentUpdate() ?
According to react docs shouldComponentUpdate will be called on three instance
state changed
props changed
state and props changed
So if only props are changing, your current state will be equal the last unmodified state.
shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate() is used.
The first question that you asked are depends on your style of data management for your state.
React lets you use whatever style of data management you want, including mutation.
And the docs also states that
shallow merge of stateChange into the new state, e.g., to adjust a shopping cart item quantity
But looking at the internal parts of setState beginUpdateQueue method let me doubtful when state gets mutated.
I think it would be wise to use immutability-helper for this part of the code, if you must guarantee that no mutation happens to the data.

React - updating state during render produces errors

I'm new to React and am trying to update the state of a parent component from the child everytime an onChange action happens. The onchange action comes from an input box that when letters are typed it updates the state of searchInputVal with the value of what has been typed. I have a parent <App/> component with the following properties and states here:
updateSampleFilteredState(filteredSamples) {
this.setState({
samples: filteredSamples
});
},
getInitialState () {
return {
samples:allSamples,
searchInputVal:""
}}
I pass the properties and states down to a child component here:
updateNewSampleState(filteredSamples){
return (
this.props.updateSampleFilteredState(filteredSamples)
)
}
render() {
const filteredSamples = this.props.samples.filter(sample => {
return sample.sampleFamily.toLowerCase().indexOf(this.props.searchInputVal.toLowerCase()) !== -1;
});
this.updateNewSampleState(filteredSamples);
return <div className="samples-container-inner-styling">
{
filteredSamples.map((sample) => {
return (...
Before I added the line this.updateNewSampleState(filteredSamples); the child component would render out the filtering just fine but obviously not update the state of sample with the new filtered state. When I the line this.updateNewSampleState(filteredSamples); to execute the function in the component to set the new state I get a list of re-occuring errors that eventually make my app crash. The errors say something about an anti pattern. I'm not sure how else to update the state?
You should't be updating the state from the render function, and you are facing the reason why that's a bad way to do things. Every time you call the setState the component re-renders, so if you call it inside the render function it will be called again and so on... You should ask yourself why are you calling that function there. I guess you could just do it in the onChange function you are using for the input.
As already mentioned by #César, setting the state in the renderer doesn't make sense, since setting the state triggers a rerender of the component, so you basically get something like an infinite render loop.
Given that you are computing filteredSamples only from the props, you could compute that state in the constructor:
The constructor is the right place to initialize state.
However, note the following when deriving state from props in the constructor:
It's okay to initialize state based on props if you know what you're doing. [...]
Beware of this pattern, as it effectively "forks" the props and can lead to bugs. Instead of syncing props to state, you often want to lift the state up.
If you "fork" props by using them for state, you might also want to implement componentWillReceiveProps(nextProps) to keep the state up-to-date with them. But lifting state up is often easier and less bug-prone.

Is it a good practice to modify a component's state and then call "setState(this.state)"?

I'm using ReactJS.
I have a stateful component (stopwatch container) and multiple child components which are stateless (stopwatches).
In the outer component, I'm doing something like this:
// the outer component is a "container" for multiple stopwatches
tick: function() {
for (var i = 0; i < this.state.stopwatches.length; i++)
{
// if a particular stopwatch is "started", I'll increase it's time
if (this.state.stopwatches[i].status == "started")
{
this.state.stopwatches[i].time.seconds++;
// do some processing
}
}
// this is the suspicious code!
this.setState(this.state);
}
Notice that I'm changing the this.state property and then calling setState() on the state object itself. This seems so wrong to me. But, in the other hand, in order to not manipulate the state object itself, i'd have to clone it and then do setState(stateClone), but I'm not sure if it's possible to clone objects effectively in JS, neither if I really should do this.
Can I continue to do setState(this.state) ?
Instead of calling this.setState(this.state) you can just call this.forceUpdate().
Just remember that this is not recommended way of updating components state. If you are unsure about your modifications take a look on Immutability Helpers.
Just my two cents here: React will re-render every time you call setState:
boolean shouldComponentUpdate(object nextProps, object nextState)
returns true by default. If you don't wish to re-render make shouldComponentUpdate(object nextProps, object nextState) to return false.
Quoting from Facebook docs:
By default, shouldComponentUpdate always returns true to prevent
subtle bugs when state is mutated in place, but if you are careful
to always treat state as immutable and to read only from props and
state in render() then you can override shouldComponentUpdate
with an implementation that compares the old props and state to
their replacements.
Aside from immutability (which is always good), the second best would be this.setState({stopwatches: this.state.stopwatches}).
With immutability helpers, it'd look like this:
var stopwatches = this.state.stopwatches.map(function(watch){
return update(watch, {
time: {$set: watch.time + 1}
});
});
this.setState({stopwatches: stopwatches})
Or with es6 you can save a few characters.
var stopwatches = this.state.stopwatches.map(watch => update(watch, {
time: {$set: watch.time + 1}
});
this.setState({stopwatches})

Categories

Resources