UNSAFE_componentWillReceiveProps always triggering every input value change - reactjs

UNSAFE_componentWillReceiveProps is always triggering for every input value change which is placed in the component itself.
As per my knowledge, it should be triggered only based on its props value change form the parent component.
Can someone help me with a hint, or please correct me if my understanding is wrong?

From the React docs:
Using this lifecycle method often leads to bugs and inconsistencies.
Use componentDidUpdate instead:
componentDidUpdate(prevProps, prevState) {
if (this.props.someProp != prevProps.someProp) {
// The property someProp changed
// Do something in response
}
}

componentWillReceiveProps takes an argument like;
componentWillReceiveProps(props){
const {someprop} : this.props;
if(someprop !== props.someprop){
//do whatever you want with the refreshed someprop
}
}

Related

Modify Child state from parent just once

I have created a <Form> Component that makes all the form-fields you give to it controlled by injecting value and onChange props to the form-fields by iterating through them. This has been working perfectly well for me for most of the forms I have created.
When I needed to have the functionality of the form-field values controlling some aspect of the parent state, I added a onFormValueChange prop to the Form that would get called whenever a field value gets updated. Using this I can track a subset of the changes to the Form's state.
However, now my problem is this...how do I override the value of a form-field, conditional on some event that occurs in the parent. I have not been able to figure out how to override the Form's state just once. If I give it a prop that sets an override value like {name: string, value: any}, then on every update this override value will override the form-field's value which is not good.
These are the solutions I thought of but they seem extremely hacky and I was hoping someone in the SO community can help.
Set an override prop on the Form Component which times out after around 100ms and hope that the user doesn't try to modify the form in that tiny duration. But I dislike using setTimeout for hacks like these.
Pass a disableOverride function along with the overrideValue prop. Then in my Form's shouldComponentUpdate I can just call disableOverride() in the callback of the setState I will use to override the value. Something like:
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.override) {
const { name, value } = nextProps.override;
const newState = Object.assign({}, this.state, { [name]: value });
this.setState(newState, () => {
nextProps.disableOverride();
});
return false;
}
return true;
}
But this also feels unnecessarily complicated, and possibly quite vulnerable to crashing unexpectedly.
EDIT Some further clarification: The point of this would be so that for example if I have 'country' and 'city' fields then if 'country' is cleared, then the 'city' should get cleared too. I can track the state of 'country' with onFormValueChange but don't have an API to modify the Form state in order to clear the 'city' field.
I came up with a solution. It was absurdly simple, I dont know why it took me so long.
componentDidUpdate(prevProps, prevState) {
if (!isEqual(prevProps.override, this.props.override)) {
const { name, value } = nextProps.override;
this.setState({
[name]: value
});
}
}
isEqual is an object comparison function taken from lodash

set state in a callback of an async function

I am new to React, so bear with me please. I have a component that calls another component that takes a property. This property will get it's value on a callback of a function, something like this:
render(){
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp))
this.setState({myProp: p})
});
return <MyComponent myProp={this.state.myProp}/>
}
myFunc will or will not make an API request and depending on that will call the callback sooner or later. This seems to work fine when API request is made and the callback takes longer to return. However, when the request is not needed and callback returns instantaneously (or almost) I am getting a Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What am I doing wrong and what is the right way to approach this? Where would be the right place to put this code? Basically what I need is to re-render MyComponenent if this.state.myProp changes
You shouldn't be calling setState inside the render method, you might end up having an infinite loop.
The call to myFunc should be somewhere else (depending on the business logic you have). When the function finishes, it will update the state and then trigger a re-render so MyComponent will get the latest value.
UPDATE
I don't know which conditions will require calling myFunc again, but you can do:
state = {
myProp: null // or some other value that MyComponent can handle as a null state
}
componentDidMount () {
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp)) // This is needed only if you get null back from the callback and you don't want to perform an unnecesary state update
this.setState({myProp: p})
}
}
render(){
const { myProp } = this.state
// You can also do if (!myProp) return null
return <MyComponent myProp={myProp}/>
}

Bind method to react state changes

Given the code below, I would like the transform() method to run anytime this.props.code changes.
class Editor extends Component {
render() {
return (
<div id="pseudo-editor" />
);
}
transform() {
var editor = ace.edit("pseudo-editor");
editor.setValue(this.props.code,1);
}
}
I am using react-redux and the state to props binding works as intended.
But Im not quite sure how to approach method binding. I guess its not an alternative to fit my JS code editors API calls inside the render method. Problably a simple solution to this one but could not find an example of which pattern to use here. Thankful for any help.
Use componentWillReceiveProps lifecycle method, it will get called whenever any change happens to props values, check the previous and nextProps values if they are not same call the transform method.
Like this:
componentWillReceiveProps(nextProps){
if(this.props.code != nextProps.code)
this.transform();
}
As per DOC:
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.

Reactjs every time refreshing page on setState

I'm using MultiSelect Plugin for Select DropDown. Here onChange event I'm storing the value in setState. Here is below code :
$('#selectbox-id-onChange').multiselect({
onChange: function(option, checked) {
var newArray = this.state.options.slice();
newArray.push($(option).val());
this.setState({options: newArray});
}
});
The above code is working fine, I'm able to store the array value in state also. But the only issue is that page is refreshing everytime while storing the value in state.
Please do let me know where I'm going wrong here. I tried to figure out but couldn't get the solution.
Yes, that's what React is meant to do. If you do not want it not to re-render then you have to override the shouldComponentUpdate method.
shouldComponentUpdate() {
return false; // Will cause component to never re-render.
}

Best practice for updating newState via shouldComponentUpdate?

I've created a text input that only updates it's own state when you're typing via onChange, and then works it's way up the parent via onBlurEvent to prevent re-rendering too much of my GUI when a user is typing.
This works, but my drawback is that since the input has it's own state, if the props change because of external forces, and I want update my text input to be those new props, I'm in a bit of a pickle. From what I've been looking up, shouldComponentUpdate can create a fix, and my code works, but just because it works doesn't mean that my implementation is good.
When using react, I understand that we should not update the state by doing:
this.state.value = "This is a new value"
and instead do:
this.setState({
value: "This is a new value"
});
But what about directly changing newState which technically isn't "this.state" inside of shouldComponentUpdate?
I have the below logic:
shouldComponentUpdate(props, newState) {
if (...logic 1 ... && props.value != newState.value) {
//keep newState
return true;
}
else if (...logic 2 ... props.value != newState.value)
{
newState.value = props.value; //Right Here
return true;
}
return false;
};
That seems just as bad as (if not worse than) setting this.state to me. You should only ever be updating your state through setState, as you're running the risk of later updates overwriting your changes otherwise.
A much better solution would be to use the componentWillReceiveProps(nextProps) lifecycle hook - the docs say:
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.
Which sounds exactly like what you're trying to do!
I don't know your logic, so I can't give a full example, but here's a starting point:
componentWillReceiveProps(nextProps) {
if (/* your condition */ && nextProps.value != this.state.value) {
this.setState({
value: nextProps.value
});
}
}

Resources