componentWillReceiveProps seems to be called twice - reactjs

In my react project, the componentWillReceiveProps() function seems to be called twice, but not sure what the problem is.
Here is the code.
import React, { Component, PropTypes } from 'react';
...
class MessagesList extends Component {
constructor(props) {
super(props);
this.state = {
message: '',
messages: []
};
...
componentWillMount() {
this.props.init_message();
};
componentWillReceiveProps(nextProps) {
this.setState({
messages: this.props.user.messages
});
var msgs = this.props.user.messages;
var total_group = [];
var msg_group = [];
var group_date = 0;
setTimeout(() => {
if (typeof msgs != 'undefined') {
for(var i = 0; i < msgs.length; i++) {
...
}
}
}, 100);
};
render() {
return (
<div className="row">
<div className="col-12">
<div className="messages">
{this.state.messages.map(message => {
return (
<div>{message.user}: {message.message} : {message.date}</div>
)
})}
</div>
</div>
...
</div>
);
}
}
I was going to read the msgs.length in the componentWillReceiveProps(), I got the following issue.
msgs.length is undefiend
After that I got the values of array, so I think the componentWillReceiveProps() seems to be called twice. So in the first call, can't read the value and then in the second call, read the value at least.
Please help me.

componentWillReceiveProps is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. Make sure to compare the current and next values if you only want to handle changes.
You will get the details from react docs.

ComponentWillReceiveProps is a method from the growth/update phase of the React lifecycle.
The Growth phase is triggered in three different ways: changing of props, changing of state or calling forceUpdate().
The value you are referring to in componentWillReceiveProps, this.props.user.messages, is the current value not the nextProps value.
Also something to consider is that the setState method is actually an asynchronous function. So when that setting of state takes place, it will cause another rerender.
I suspect, but I cannot be sure without more of your code, that setState is called once with your original value from props which triggers another update cycle. During this next update cycle the setState method now sets state to the new prop values.
Are you perhaps meaning to use nextProps.user.messages instead of this.props.user.messages?

Related

this.props got updated before shouldComponentUpdate run

I have my ClockBlock component where I lifted state keeped in object "timer":
class ClockBlock extends Component {
constructor(props){
super(props);
this.state = { timer: props.timer }; // timer from Redux store
}
render(){
return(
<div className="clockBlock">
<Circle timer={this.state.timer} updateTimer={this.updateTimer.bind(this)} />
<Clock timer={this.state.timer} updateTimer={this.updateTimer.bind(this)} />
<ClockTerms updateTimer={this.updateTimer.bind(this)} />
</div>
)
}
All the three nested component influence each other via updateTimer function. (except ClockTerms component - it works in one direction). The function is here:
updateTimer(newDuration){
const newState = Object.assign( {}, this.state );
newState.timer.duration = newDuration;
this.setState( newState );
}
So, the problem - when I change timer using ClockTerms component I see changes in the Clock component too (via props, apparently). In the same time in the Circle component in shouldComponentUpdate function i'm trying to see the difference between the old props and the new. Here is this function:
shouldComponentUpdate(nextProps, nextState){
console.log( this.state.timer );
console.log( nextState.timer );
console.log( this.props.timer );
console.log( nextProps.timer );
return true;
}
All the console.log() calls print the same data - new props. Why? And how can I get old props?
PS: i simplified my code above, removing irrelevant from my point of view calculations. Can give whole code, if it is important.
I also use Redux here, but it seems to me it isn't engaged here much.
Great thanks in advance!
UPDATE: I also get the same picture when place the same shouldComponentUpdate function in ClockBlock (parent) component;
You can functionally set state. Because setState is async, when multiple things call setState React uses the most recent properties passed to setState.
Instead you can update state by passing an updater function. This batches all the state updates. When Reacts lifecycle begins to update state it'll process all the pending updater functions in sequence.
This is from the docs describing what happens when setState uses an object.
Subsequent calls will override values from previous calls in the same
cycle, so the quantity will only be incremented once. If the next
state depends on the current state, we recommend using the updater
function form, instead:
this.setState((state) => {
return {quantity: state.quantity + 1};
});
https://reactjs.org/docs/react-component.html#setstate
I've solved a similar situation by comparing this.state to nextProps and updating state. I don't think it's a great solution, but didn't come up with anything else jet.
state = {
dayOff: this.props.myProperty
}
shouldComponentUpdate(nextProps, nextState) {
const shouldUpdate = this.state.myProperty !== nextProps.myProperty;
if (shouldUpdate) this.state.myProperty = nextProps.myProperty
return shouldUpdate;
}
The point is that updateTimer fires BEFORE shouldComponentUpdate runs. That's it. So, when you update your lifted state in the parent component from children components (via passed in props function), keep in mind that shouldComponentUpdate will get already changed state and props.

Method called after latest render

In my reactjs application, in one component, render function is called many times. I have some data available only at the last time the render is called. I need to do some treatments on this data. Where can I do it?
componentWillMount() {
get(this.props, 'myarraydata', []).forEach(element => {
//here i will do some treatments for each elemment
});
}
render() {
console.log(get(this.props, 'myarraydata', []));
return (
<div>
<pre>{JSON.stringify(get(this.props, 'myarraydata', []), null, 2) }</pre>
</div>
);
}
As you can see, in my application, this render method is called many times, and it uses myarraydata passed from another component and myarraydata is available only when render is called for the last time (means when render is called for the first, second, .. times, my arraydata is empty). but the problem is that componentwillmount method is called only one time, and at that time, myarraydata still empty and i can't do the treatments i need
componentDidUpdate lifecycle hook is called after each subsequent render, as it can be seen on this diagram:
otice that it isn't called on initial render when componentDidMount is called instead after render hook.
Data that was received via props can be processed there, this requires to call setState or forceUpdate then to make it rendered.
In original code, this requires to introduce the state to this component. Also, componentWillMount was deprecated because it was commonly misused for cases like this one. It should be replaced with constructor code or componentDidMount. In this case the state synchronously derives from myarraydata prop, this is what getDerivedStateFromProps is for:
state = { processedData: null };
static getDerivedStateFromProps(props, state) {
if (conditionsToNotProcessData) return null;
const processedData = props.myarraydata || [];
for (const item of processedData) { ... }
return { ...state, processedData };
}
render() {
return (
<div>
<pre>{JSON.stringify(this.state.processedData)}</pre>
</div>
);
}
Try using the componentDidMount lifecycle method.
Need elaboration on
I have some data available only at the last time the render is called
What does this mean exactly?
EDIT : Try this :
componentWillMount() {
let data = get(this.props, 'myarraydata', [])
if(data.length !== 0){
data.forEach(element => {
//here i will do some treatments for each elemment
});
}
}

Unable to setState to React component

I'm trying to set the state of my PlayerKey component here however the state won't update on a onClick action:
class PlayerKey extends Component {
constructor(props) {
super(props);
this.state = {
activeKeys:[]
}
}
activateKey = (e) => {
this.setState({
activeKeys:["2","3"]
})
}
render() {
return (
<div className="key" data-row-number={this.props.rowKey} data-key-number={this.props.dataKeyNumber} onClick={this.activateKey}></div>
)
}
}
I've tried console logging this.state in activateKey and it gives me the state of the component no problem (the blank array) so not sure why I can't update it?
setState method in react is asynchronous and doesn't reflect the updated state value immediately.
React may batch multiple setState() calls into a single update for performance.
So accessing the recently set state value might return the older value. To see whether the state has really been set or not, You can actually pass a function as callback in setState and see the updated state value. React Docs
As in your case, you can pass a function as callback as follows.
activateKey = (e) => {
this.setState({
activeKeys:["2","3"]
}, () => {
console.log(this.state.activeKeys); // This is guaranteed to return the updated state.
});
}
See this fiddle: JSFiddle

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.

Statement is not being reached after setting State in react ES-6

In this react code i am trying to set the data to current state,After setting the data to state i need to use it in fixed data table.To achieve it, i wrote as below.The main issue is that the statement next to "this.setState" in componentDidMount is not being executed/reached where i need to call data table function there.
here is my code:
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
result: [],
loading: false,
filteredRows: null,
filterBy: null,
};
}
getZipData() {
//Here i got the data from server
}
componentDidMount() {
this.getZipData().then((data)=>{
console.log("data:"+data.length);
this.setState({result:data,loading:true}); //state successfully set here.
console.log("result:*******************"+this.state.result.length); //this console is not printed
this._filterRowsBy(this.state.filterBy)
});
};
_rowGetter(rowIndex){
alert("row Getter");
return this.state.filteredRows[rowIndex];
}
_filterRowsBy(filterBy){
var rows = this.state.result.slice();
var filteredRows = filterBy ? rows.filter((row)=>{
return row.RecNo.toLowerCase().indexOf(filterBy.toLowerCase()) >= 0
}) : rows;
this.setState({
filterRows,
filterBy,
})
}
_onFilterChange(e){
this._filterRowsBy(e.target.value);
}
render() {
if(this.state.loading==false) {
return (
<div className="row-fluid">
<img className="col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3"
src="http://www.preciseleads.com/images/loading_animation.gif"/>
</div>
)
}
else{
console.log("result:////////////////////"+this.state.result.length); //this console is printed
console.log("loading completed");
return(
<div>
<!-- fixed data table code goes here -->
</div>
)
}
}
}
state is successfully updated but after that statement is not reached, IN componentDidMount,
any help much appreciated.
Add a catch handler to your Promise. I suppose you are getting an error.
Hi there maybe this is your problem,
Facebook:
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. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains. setState() will always trigger a
re-render unless conditional rendering logic is implemented in
shouldComponentUpdate(). If mutable objects are being used and the
logic cannot be implemented in shouldComponentUpdate(), calling
setState() only when the new state differs from the previous state
will avoid unnecessary re-renders.
this.setState({result:data,loading:true}, () => {
// Your logic
});
the second argument is a callback function which is called after the state is set.
Hope this helps:)
Edit: i didn't see the invariant error, (From the comment, could u provide the full error)

Resources