How to create an event with state update (by getDerivedStateFromProps) - reactjs

I am trying to change componentWillReceiveProps method to static getDerivedStateFromProps and can't figure out how to use the keyword this inside the getDerivedStateFromProps function.
Originally, I wanted to use this.props.history.push() inside the getDerivedStateFromProps but could not figure out how.
I then tried returning state (as getDerivedStateFromProps is supposed to do) and could not create an event with it. A brief look at the code will make my question more comprehensible.
static getDerivedStateFromProps(nextProps, prevState){
if(nextProps.passwordResetResult.message==="Password reset
Successfully")
{
alert("Password reset successfully please login again");
this.props.history.push('/home')
}
}
I know I am supposed to return state at the end; but I didn't find it useful for my purpose, as don't understand how do I create an event which will be triggered on state change after return and then trigger this.props.history.push().
Above code caused error
this.props is undefined.
I understand I can't use this keyword inside getDerivedStateFromProps; am I correct in assuming that?

getDerviedStateFromProps is a static function, so you can't access this from it, static functions are bound to the class itself and not the instance of the component. I believe its intentionally done this way to avoid side effects.
So Instead of using this.history.push you should use nextProps.history.push which you get in the lifecycle hook arguments.
But as for your use-case since you said you don't really need to return state, it means you don't really need derived state because you don't change internal state based on specific props. You should use componentDidUpdate instead for side-effects, see https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops

Related

How to intercept setState function to capture individual state changes?

I am implementing activity logging in a React application to track users activity and a practical way would be to simply log each change to the component state. The state object is large in my scenario, so I would like to capture only the changed elements, which would also be easier to read in the log. To do so it would be ideal to intercept every setState call so that I can get the specific pieces that changed in the state. I have most of all the state in a higher level component so I would have to do this only in one component basically.
I don't want to do this in every call to setState across all my component's code. I want to be able intercept it for once an for all at a component level.
Is there an elegant way of intercepting setState that does not involve creating an alternate setState function stub?
Any tips greatly appreciated. Thanks.
The following lifecycle methods will be showing the changes when state/props changes in a component. You can use the state object to determine the activity logging.
// Happens before the component update
componentWillUpdate(object nextProps, object nextState)
// Happens after the component update
componentDidUpdate(object prevProps, object prevState)
For example,
componentDidUpdate(prevProps, prevState) {
if(prevState.isLoadingData !== this.state.isLoadingData) {
writeToActivityLog('user-requested-new-data');
}
}
The state object is large in my scenario, so I would like to capture
only the changed elements, which would also be easier to read in the
log.
For this scenario, you can make use of utility library like lodash to get all the changed properties or do a deep equal between this.state and prev/nextState. Even then you may require conditions to log different events.

React global state update local state

I need to update the local state after a global state is updated.
I tried mapStateToProps but this only maps the global state into the component, it does not update the local state.
Please refer to image to see the code.
after mapStateToProps is updated, the values on the selected_smsf_member should be parsed into the local state which is used to create the form.
I am also open to suggestions of a better approach. Basically what I am trying to do is to show the details of a selected member on a previous component.
As you can see mapStateToProps, its name says everything, It will map state to props, you can access its variable in your props, like this.props.selected_smsf_member in your component. If you still want to update your local state, using getDerivedStateFromProps(), but read this blog first: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
You could utilize getDerivedStateFromProps. You can check your props and derive state from it when they change. A few notes though:
You should initialize your state.memberDetailsForm as a falsey value and check that in your getDerivedState function otherwise it'll derive your state EVERY TIME the props change which would lead to unexpected consequences.
I don't know the exact mappings to your code so don't just copy the
example below and expect it to work.
Implementing this method should get you the output that you want.
static getDerivedStateFromProps(props, state) {
if (!state.memberDetailsForm) {
return { memberDetailsForm: props.selected_smsf_member }
}
}

componentWillUpdate -- is it safe to modify nextProps or nextState?

What are the implications of doing this?
class Foo extends React.Component {
componentWillUpdate(nextProps, nextState) {
nextState.bar = transformSomehow(nextState.foo);
}
}
My gut tells me that this is wrong, but I haven't found any documentation saying that nextProps and nextState should be read-only, nor have I found anything saying that this is ok.
The idea here is that I want some kind of sanitized/transformed/modified version of data from state. There are probably plenty of other (better) ways to do this, but is this also a viable option?
I would say that this is not an ideal approach because you are trying to mutate state. You should not call setState from within componentWillUpdate, nor should you try to update your state object at this point. This stage is used more for animations, DOM manipulations, etc.
If you want to set some other vars based on nextState, go for it. If you want to work ad hoc copies of state to get something done, all good. But I think that attempting to mutate state in this method runs counter to the intended use of this lifecycle method. From the docs on componentWillUpdate:
Note that you cannot call this.setState() here. If you need to update state in response to a prop change, use componentWillReceiveProps() instead.

Determine when Relay has fetched data and populated props

When I call this.props.relay.setVariables, Relay will fetch data given these new variables, and afterwards I want to do stuff with this data. However I cannot figure out a good way to know exactly when this occur.
I first tried using the onReadyStateChange callback on setVariables but this didn't catch the moment when props was populated.
What I ended up doing to fix the problem was using a setTimeout in componentWillRecieveProps and by then the props is populated with the new data. But is there a better way to determine when data has been fetched?
There is no need to invoke setTimeout inside of componentWillReceiveProps(nextProps) as the lifecycle method gets called automatically once the component's props change so you just need a conditional block like this in the componentWillReceiveProps (assuming the variables can be distinguished via === check)
if(this.props.data !== nextProps.data){
//do your stuff
}
or you could use the length field if the data is an array:
if(this.props.data.length !== nextProps.data.length){
//do your stuff
}
If the condition holds, it means that you've received the new data you were looking for and you can process it.

ReactJS: Why shouldn't I mutate nested state?

I've read the ReactJS documentation about setState. Specifically, this line:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
But in my code, I do things like this:
var x = this.state.x;
x.y.z.push("foo"); // z is a list
this.setState({x:x});
This code seems to work and update state correctly. But according to the documentation, I'm breaking the rules. What's the problem with this approach? Are there performance issues? Race conditions? Will I be scolded by the Facebook dev team? Ideally, I'd like to avoid the immutability helpers and all the other craziness and keep things simple.
By manipulating state directly, you are circumventing React's state management, which really works against the React paradigm. The most significant benefit of React's setState is that it immediately triggers a re-render, which merges your DOM changes from the shadow DOM into the document DOM. So, you capture all the state changes you need in some handler, then build up the state changes you want in a literal object, then pass that to setState. This is not quite what your example above does.
So while the code sample you provided technically breaks this rule, since you are calling setState directly after mutating through a reference to a state object property, your changes are immediately being funneled through React's state management. So, everything will work as you expect. The idea is that you don't want to get in the habit of making many changes to state in this way, it's better to capture your intended state in a new literal object or array, then set it to the state once (i.e., with no previous mutations of state) through a call to setState, which will trigger a single re-render.
EDIT: To more definitively answer your question, I would add that the real concern is that a dev would directly manipulate state in many different places or methods, without calling setState, and then at some later point or other code, call setState and then wonder why their render isn't producing the results they expected. Since setState does an object merge between the known, managed state, and the literal object passed as an argument to setState, it's possible the results would not be what you would expect if you had previously manipulated state directly.
The reason is that you miss out on the callbacks associated with this.setState and they claim that you may overwrite data by just assigning straight to the object.
Also, often in OOP (I don't know how many stars JS gets...) you'll use getter and setter methods instead of directly manipulating the object. In this example via this.state.prop1 = "xyz";
I've never had react overwrite my directly set state properties. The temptation to code it this way may be write to state without re-rendering. But if you're doing that you may think about not putting those in state anyways.
If re-rendering performance is an issues checkout https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate
The main reason is that shouldComponentUpdate will not work correctly.

Resources