ComponentWillMount doesn't set the state React JS - reactjs

Why this isn't working :
class Slider extends React.Component {
constructor(props){
super(props);
this.state = {
top : 0,
responsiveImg : ""
};
}
componentWillMount(){
const bodyWidth = window.innerWidth;
const responsiveSliderImg = "responsiveSliderImg";
if((bodyWidth >= 415) && (bodyWidth < 473)){
this.setState({responsiveImg: responsiveSliderImg});
console.log("The state is : " + this.state.responsiveImg);
}
}
}
I'm trying to add a class name to the state in componentWillMount function, but it doesn't work. In console log I get The state is : , thus the state is empty.
Any idea?

State doesn't update synchronously. From the docs
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
Try checking the state in your render method.
It's also good to become aware of which of the lifecycle methods you can call setState in without a re-render occurring. Can be helpful in some cases. I use reactcheatsheet as a quick reference. Click the "lifecycle" filter to see a very helpful table reference.
As a matter of interest. I have developed a utility library to help with responsive use cases similar to yours. It provides your component with the size of it's container allow you to do responsive render logic. It could be helpful to you? Check it out here.

setState is not immediately update state., if you need get new state you can pass callback as second argument to setState
this.setState({responsiveImg: responsiveSliderImg}, function () {
// get new state here
});
Example

Related

React-Native: Can we just do state assignment with out state update?

class Myclass extends Component {
constructor(props) {
super(props);
this.onLoad();
}
onLoad() {
this.state.temp = 'Some value';
}
}
the state cannot be set in the constructor itself as it need to be set based on some conditions when onLoad() is called multiple times. And when the value is assigned component re rendering is also not required.
is assigning a value to state with out using setState good practice? if not, What would be cons?
this.state.x = 'something' is definitely not the right way to set the state. Also, even though you can do it, it is not recommended to perform side-effects in the constructor what so ever as it slows down the mounting times. Refer to: https://reactjs.org/docs/react-component.html#constructor
So I would recommend you to perform your side-effects in componentDidMount. You could do something as follows:
class Myclass extends Component {
constructor(props) {
super(props);
this.onLoad = this.onLoad.bind(this);
}
componentDidMount() {
this.onLoad();
}
onLoad() {
this.setState({ temp: 'Some value'});
}
}
Again, you can't use this.state.temp = 'some val' to change states in React because it doesn't re-render the component. You will always need to use setState to make sure your component re-renders. Refer to this link for more information.
Let me know if this has helped, and if not, I will happily answer your queries in comments below. :)
I don't know why you have to set in another function.
what's for?
you have to use setState to change states in not constructor.
anyway, you can use callback function of setState if you need to response right away after changing state.
setState(
{
temp: 'Some value'
},
() => console.log(this.state)
)
or just use variables inside the class.

Reactjs: updating state immediately

Complete Reactjs newbie here. I know setState() is asynchronous. I know if I do setState() then the states are queued and batched, so I will not see the state changed immediately. Fair enough.
I have also read the following link and other questions in SO:
https://reactjs.org/docs/react-component.html#setstate
I can use: callback methods in setState, componentDidUpdate() lifecycle method, concat if the state is an array etc. I get all these ways. My problem is a simple one, also I am banging my head on this issue for past 2 days so I am literally at my wit's end now.
I have this logic:
class ItemList extends React.Component {
constructor(props) {
super(props);
this.state = {showCompleted: false};
this.shouldShowFullList = false;
this.onShowCompleted = this.onShowCompleted.bind(this);
}
onShowCompleted(event) {
this.setState({showCompleted: event}, () => {this.shouldShowFullList = this.state.showCompleted;});
}
render() {
if (this.shouldShowFullList) {
return this.renderAllItemsIncludingCompleted();
} else {
return this.renderOnlyNotCompletedItems();
}
}
...
The logic is self-explanatory. My problem is even when I call this.shouldShowFullList in callback method of setState(), it still does not show an updated value. Value of this.shouldShowFullList is false when it should be true and vice versa. What is the best way to have the value of this.shouldShowFullList in lockstep with the this.state.showCompleted?
NOTE: onShowCompleted() is a callback method triggered from a child component. When a checkbox called "Show Completed" is checked, I should show a complete list of items, or else just the items which are not completed - something like ToDo list.
At onShowCompleted do
this.setState({ showCompleted: true })
or if you want toggle the value
this.setState({ showCompleted: !this.state.showCompleted })
Then in the render method you can do
if (this.state.showCompleted) {
return this.renderAllItemsIncludingCompleted();
} else {
return this.renderOnlyNotCompletedItems();
}
When you set a state using setState the method render is called with this.state updated. I think the callback (second argument) of this.setState is called after the render.
Because updating this.state make a new render it seems that react is pushing you to use this.state in the render method. In fact it's made for this usage. If you want to make a variable that have no purpose in the render you can use this.myVariable. Best pratice is to use only this.stateand this.props in the render (or function that depend of it).

React: Updating Component State with Direct Value Assignment - Any reason not to?

I am new to React, and am storing information needed only by my component in the component's state. I am running into a problem with the asynchronous nature of the this.setState call.
I have made my way around this by just assigning the state value directly, with this.state.stateKey = newValue. See below for a specific short sample case that demonstrates how this.setState does not work for me and the direct assignment does.
import React from 'react';
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
testVal : "Enter new text here"
}
}
runTest = (evt) => {
// if I run the test with the below line, the value
// in state is not updated before the console.log
this.setState({testVal: evt.target.value});
// if I run the test with the below line, the value
// in state is loaded before the console.log call,
// and I do get a print of the new value
this.state.testVal = evt.target.value;
console.log("testVal: ", this.state.testVal);
}
render = () => {
return(
<div>
Test
<input
class="form-control"
type="text"
defaultValue={this.state.testVal}
onBlur={this.runTest}
/>
</div>
)
}
}
My question is there anything wrong with updating state with direct assignment using this.state.stateKey = newValue? I see other workarounds, but no mention of this. And it seems so simple that there must be something wrong with it. Thanks.
Creating an immutable clone of state is a good idea because of the way state changes are compared in order to optimise rendering.
In lifecycle methods like shouldComponentUpdate, nextProps are passed in and can be compared to this.props.
If you mutate the state directly, then nextProps.someprop and this.props.someprop will always be the same and therefore you might not get the expected behaviour.
The React doc also says
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

React Child Component Not Updating After Parent State Change

I'm attempting to make a nice ApiWrapper component to populate data in various child components. From everything I've read, this should work: https://jsfiddle.net/vinniejames/m1mesp6z/1/
class ApiWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
response: {
"title": 'nothing fetched yet'
}
};
}
componentDidMount() {
this._makeApiCall(this.props.endpoint);
}
_makeApiCall(endpoint) {
fetch(endpoint).then(function(response) {
this.setState({
response: response
});
}.bind(this))
}
render() {
return <Child data = {
this.state.response
}
/>;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
};
}
render() {
console.log(this.state.data, 'new data');
return ( < span > {
this.state.data.title
} < /span>);
};
}
var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;
ReactDOM.render(
element,
document.getElementById('container')
);
But for some reason, it seems the child component is not updating when the parent state changes.
Am I missing something here?
There are two issues with your code.
Your child component's initial state is set from props.
this.state = {
data: props.data
};
Quoting from this SO Answer:
Passing the intial state to a component as a prop is an anti-pattern
because the getInitialState (in our case the constuctor) method is only called the first time the
component renders. Never more. Meaning that, if you re-render that
component passing a different value as a prop, the component
will not react accordingly, because the component will keep the state
from the first time it was rendered. It's very error prone.
So if you can't avoid such a situation the ideal solution is to use the method componentWillReceiveProps to listen for new props.
Adding the below code to your child component will solve your problem with Child component re-rendering.
componentWillReceiveProps(nextProps) {
this.setState({ data: nextProps.data });
}
The second issue is with the fetch.
_makeApiCall(endpoint) {
fetch(endpoint)
.then((response) => response.json()) // ----> you missed this part
.then((response) => this.setState({ response }));
}
And here is a working fiddle: https://jsfiddle.net/o8b04mLy/
If the above solution has still not solved your problem I'll suggest you see once how you're changing the state, if you're not returning a new object then sometimes react sees no difference in the new previous and the changed state, it's a good practice to always pass a new object when changing the state, seeing the new object react will definitely re-render all the components needing that have access to that changed state.
For example: -
Here I'll change one property of an array of objects in my state, look at how I spread all the data in a new object. Also, the code below might look a bit alien to you, it's a redux reducer function BUT don't worry it's just a method to change the state.
export const addItemToCart = (cartItems,cartItemToBeAdded) => {
return cartItems.map(item => {
if(item.id===existingItem.id){
++item.quantity;
}
// I can simply return item but instead I spread the item and return a new object
return {...item}
})
}
Just make sure you're changing the state with a new object, even if you make a minor change in the state just spread it in a new object and then return, this will trigger rendering in all the appropriate places.
Hope this helped. Let me know if I'm wrong somewhere :)
There are some things you need to change.
When fetch get the response, it is not a json.
I was looking for how can I get this json and I discovered this link.
By the other side, you need to think that constructor function is called only once.
So, you need to change the way that you retrieve the data in <Child> component.
Here, I left an example code: https://jsfiddle.net/emq1ztqj/
I hope that helps.
Accepted answer and componentWillReceiveProps
The componentWillReceiveProps call in accepted answer is deprecated and will be removed from React with version 17 React Docs: UNSAFE_componentWillReceiveProps()
Using derived state logic in React
As the React docs is pointing, using derived state (meaning: a component reflecting a change that is happened in its props) can make your components harder to think, and could be an anti-pattern. React Docs: You Probably Don't Need Derived State
Current solution: getDerivedStateFromProps
If you choose to use derived state, current solution is using getDerivedStateFromProps call as #DiogoSanto said.
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing. React Docs: static getDerivedStateFromProps()
How to use componentWillReceiveProps
This method can not access instance properties. All it does describing React how to compute new state from a given props. Whenever props are changed, React will call this method and will use the object returned by this method as the new state.
class Child extends React.Component {
constructor() {
super(props);
// nothing changed, assign the state for the
// first time to teach its initial shape.
// (it will work without this, but will throw
// a warning)
this.state = {
data: props.data
};
}
componentWillReceiveProps(props) {
// return the new state as object, do not call .setState()
return {
data: props.data
};
}
render() {
// nothing changed, will be called after
// componentWillReceiveProps returned the new state,
// each time props are updated.
return (
<span>{this.state.data.title}</span>
);
}
}
Caution
Re-rendering a component according to a change happened in parent component can be annoying for user because of losing the user input on that component.
Derived state logic can make components harder to understand, think on. Use wisely.

What will happen if I use setState() function in constructor of a Class in ReactJS or React Native?

Out of curiosity, I just wanna know what will happen if I use setState() function in constructor of a Class in React Native or ReactJS?
Such as:
constructor(props) {
super(props);
this.setState({title: 'new title'});
}
what's the lifecycle of React gonna happen?
I haven't read the code inside React. I am afraid it will some any damage when I write it that way.
What setState essentially does is to run a bunch of logic you probably don't need in the constructor.
When you go state = {foo : "bar"} you simply assign something to the javascript object state, like you would any other object. (That's all state is by the way, just a regular object local to every component).
When you use setState(), then apart from assigning to the object state react also rerenders the component and all its children. Which you don't need in the constructor, since the component hasn't been rendered anyway.
Error Message would be
Uncaught TypeError: Cannot read property 'VARIABLE_NAME' of null
Please see the following two jsfiddle snippets.
Case 1) Working solution jsfiddle
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'world'
}
}
render() {
return <div>{this.state.name} </div>
}
}
ReactDOM.render(<Hello />, document.getElementById('container'));
Case 2) Not working solution
class Hello extends React.Component {
constructor(props) {
super(props);
this.setState({
name: 'hello'
});
}
render() {
return <div>{this.state.name} </div>
}
}
ReactDOM.render(<Hello />, document.getElementById('container'));
This happens:
-constructor (starts setState)
-render (fails - because state has no property 'name' yet)
-name is added to state (but too late - it has already rendered)
Conclusion:
My rule of thumb, inside constructor uses this.state = {} directly, other places use this.setState({ });
Constructor: Constructor is Used to initialize the state.
State : Components that contain local state have a property called "this.state".
SetState: React components have a method available to them called setState Calling "this.setState" causes React to re-render your application and update the DOM.you can also track of prevstate in setState
If you use setState in constructor you would get error like this:Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component.
React have not restricted the use of setState in any lifecycle event.
React official docs link for state update
It was told that you cannot access state directly outside constructor .
So you are free to call setState anywhere .
Technically setState is meant to update the existing state with some new value passed within ,which react react handles in the next update cycle through batch process,so using console.log of state right after setState will give the stale value.
Now lets focus on what if we call setState in the constructor .
React will prepare the batched piece of code with the new state passed into the setState and trigger a update .
While react have not stopped you from doing this ,however it knows that doing so might land you up in trouble so leaves a nice message for you
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the component.
React proceeds with further loading of component but the update never reflects for this batch.
So conclusion : If you try to do so you might not end up with error ,but you will have to bear the undesirable behavior as these updates will reflect no where even if triggered .

Resources