Set Props from External Source - reactjs

When a UI event occurs, my top-level React component receives data to use as props from an external object. I'd like to know the correct way to update the component's props to use for the next render.
It seems like one of the component lifecycle methods should handle this, but that doesn't seem to be the case.
The code below shows what I've tried:
• Root.update: Custom method invoked externally once the data is ready. Both of the two techniques shown do work.
• Root.getDefaultProps: Used to retrieve props for first render.
• Root.render: This also works, but is redundant on first render.
• Root.componentWillUpdate: Does not work but seems like it should.
• Root.componentWillReceiveProps: Wouldn't make sense for this to work; props aren't received from a React component.
var Root = React.createClass({
update: function() {
this.setProps(Obj.data); // works but setProps is deprecated
this.props = Obj.data; // always works
},
getDefaultProps: function() {
return Obj.pageload(); // works on first render
},
componentWillUpdate: function() {
this.props = Obj.data. // does not work
this.setProps(Obj.data); // infinite loop
},
componentWillReceiveProps: function() {
this.props = Obj.data; // does not work
},
render: function() {
this.props = Obj.data; // works but is redundant
// ...
},
});

At some point you must be calling React.render to render that initial root component with your props.
To update the component with new props simply call React.render again for the same component on the same Dom element with the new props.
This won't re-mount the component but will in fact simply send the mounted instance your new props and cause it to re-render with them.
If you want to control what happens when your component receives props then take a look at the component lifecycle method componentWillReceiveProps.

Related

`componentWillReceiveProps` explanation

I recently wanted to upgrade my knowledge of React, so I started from the component lifecycle methods. The first thing that got me curious, is this componentWillReceiveProps. So, the docs are saying that it's fired when component is receiving new (not necessarily updated) props. Inside that method we can compare them and save into the state if needed.
My question is: Why do we need that method, if changes in props of that component (inside parent render) will trigger the re-render of this child component?
One common use case are state (this.state) updates that may be necessary in response to the updated props.
Since you should not try to update the component's state via this.setState() in the render function, this needs to happen in componentWillReceiveProps.
Additionally, if some prop is used as a parameter to some fetch function you should watch this prop in componentWillReceiveProps to re-fetch data using the new parameter.
Usually componentDidMount is used as a place where you trigger a method to fetch some data. But if your container, for example, UserData is not unmounted and you change userId prop, the container needs to fetch data of a user for corresponding userId.
class UserData extends Component {
componentDidMount() {
this.props.getUser(this.props.userId);
}
componentWillReceiveProps(nextProps) {
if (this.props.userId !== nextProps.userid) {
this.props.getUser(nextProps.userId);
}
}
render() {
if (this.props.loading) {
return <div>Loading...</div>
}
return <div>{this.user.firstName}</div>
}
}
It is not a full working example. Let's imagine that getUser dispatch Redux action and Redux assign to the component user, loading and getUser props.
It 'serves' as an opportunity to react to the incoming props to set the state of your application before render. If your call setState after render you will re-render infinitely and that's why you're not allowed to do that, so you can use componentWillReceiveProps instead.
But... you are beyond CORRECT in your confusion, so correct in fact that they are deprecating it and other Will-lifecycle hooks Discussion Deprecation.
There are other ways to accomplish what you want to do without most of those Will-lifecycle methods, one way being don't call setState after render, just use the new incoming props directly in render (or wherever) to create the stateful value you need and then just use the new value directly, you can then later set state to keep a reference for the next iteration ex: this.state.someState = someValue, this will accomplish everything and not re-render the component in an infinite loop.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
Look at this article
the componentWillReceiveProps will always receive as param "NxtProps", componentWillReceiveProps is called after render().
some people use this method use this to compare nxtProps and this.props to check, if something should happen before the component call render, and to some validations.
check the react's documentation to know more about react lifecycle!
hope this could help you!
changes in props of that component (inside parent render) will trigger the re-render of this child component
You are absolutely right. You only need to use this method if you need to react to those changes. For instance, you might have a piece of state in a child component that is calculated using multiple props.
Small Example:
class Test extends Component {
state = {
modified: "blank"
};
componentDidMount(){
this.setState({
modified: `${this.props.myProp} isModified`
});
}
componentWillReceiveProps(nextProps) {
this.setState({
modified: `${nextProps.myProp} isModified`
});
}
render() {
return <div className="displayed">{this.state.modified}</div>
}
}
In this example, componentDidMount sets the state using this.props. When this component receives new props, without componentWillReceiveProps, this.state.modified would never be updated again.
Of course, you could just do {this.props.myProp + "IsModified"} in the render method, but componentWillReceiveProps is useful when you need to update this.state on prop changes.

React Redux Lifecycle Props

I am using react and redux for an web application I am building.
For the most part everyhthing is working just fine however I have one major issue when it comes to passing props from the parent.
I have a main component which connects to redux and obtains my store. I pass the props just fine:
{ this.props.children && React.cloneElement(
this.props.children,
{
preset: this.state.preset,
children: this.state.babies,
child: this.state.currentChild,
name: this.state.firstName,
}
)}
So my particular page gets this props. I then pass needed props to child components, however I cannot access the props on mount or any other lifecycle method that react provides. The only place they seem to be available is in the render method, and thats after running a undefined check:
let child = this.props.child;
if(child.birthdate != undefined) {
// do stuff here
}
It looks like i receive undefined twice then props finally come in and I am able to work with them.
My question is what lifecycle component should i be accessing to format my data. I have ran all available methods in the docs to try to console.log and see where I am at but they all return empty. The only place I actually get the props in in the render.
My explanation is poor i know but any help would be appreciated.
-E
componentWillReceiveProps lifecycle method should do a trick. You can get a prop that is incoming and format it there. After that you can set formatted data in component state and use it in your render method like this.
componentWillReceiveProps(nextProps) {
this.setState({
formattedBirthdate: nextProps.child.birthdate
});
}
The second option will be to do the same in your constructor.
After that you can output your formattedBirthdate in render, like this:
this.state.formattedBirthdate && <div>{this.state.formattedBirthdate}</div>

How can I call forceUpdate on the root element in React?

I need to call forceUpdate on the app root of my React App or on another component than the one I am currently in. I know to call forceUpdate on the current component it is this.forceUpdate(), but how would I call it on another component?
You can pass the this.forceUpdate() function vis props to a child component
root.js
var Root = React.createClass({
render: function() {
return (
<UpdateComponent
rootUpdate={this.forceUpdate.bind(this)}
/>
);
}
});
Then you can access call this.props.rootUpdate() in your UpdateComponent whenever you need the root to be force updated.
forceUpdate is a worse case scenario and should be avoided because it deviates from the React way of doing things.
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.
Changing the key of the element you want re-rendered will work. Set the key prop on your element via state and then when you want to update set state to have a new key.
<Element key={this.state.key} />
Then a change occurs and you reset the key
this.setState({ key: Math.random() });
I want to note that this will replace the element that the key is changing on. It is always discourage to use the forceUpdate as there is always a way to avaoid this.

React componentDidMount() is fired on unMounted component

A simple react component that invokes a promise from the data store on componentDidMount is throwing a warning:
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 LocationNameView component.
I threw a few debug console.log to see if this.isMounted() is true/false, and inside componentDidMount this.isMounted() will return false the first time then again and it will be true. I'm not sure if the docs are clear or the name of componentDidMount is skewing my reasoning here but it seems like this method should only be invoked if the component is actually mounted.
enter link description here
componentDidMount: function() {
var self = this;
// make the request to the backend and replace the loading location text
Models.Location.find(this.props.location)
.then(function(location) {
console.log(self.isMounted()); // <--- shows false then true
self.setState({ location : location });
});
},
componentDidMount is invoked when the underlying DOM representation is built, but, not yet mounted to the actual DOM.
The reason the warning is being displayed about setting state on an unMounted component is because the aSync callback in the above example can return before the component is actually mounted to the DOM tree in the client/browser.
The lesson here is if you're using an aSync callback that sets state in your component on componentWillMount or componentDidMount, first use the safety catch isMounted() before proceeding to setting state, ie:
componentDidMount() {
let self = this;
PromiseMethod().then(function aSyncCallback(data) {
if ( self.isMounted() ) {
self.setState({...data});
}
});
}
React Component Lifecycle reference

React reusable stateful component

Let's say I created a component which can be turned on/off based on state.
var onOff = React.createElement(<OnOff />, mountElement);
onOff.setState({ on: false });
Later I'm creating a new component called Parent, which will use OnOff inside it.
render() { return <div><OnOff /></div> }
Now how can I change the OnOff state? There is no way I can call setState on it. And I should not according to React doc. So I have to add initial state to OnOff's props:
constructor(props) {
super(props);
this.state = { on: props.initialOn };
}
then in Parent's render method, set the initialOn prop with its state:
render() { return <div><OnOff initialOn={false} /></div> }
But it's still not working, because whenever I change Parent's state, the OnOff component inside it is not re-created with new initial state. Instead, it is only re-rendered with old state. I have a CodePen to prove it: http://codepen.io/anon/pen/QjMwjO?editors=101
You can update the state of the OnOff component by declaring the update also inside a componentWillReceiveProps function, something like:
componentWillReceiveProps:
function(nextProps) {
this.setState({
on : nextProps.initialOn
});
}
This allows you to update state, when new props arrive. And it is valid react.
You should however consider if you need state in OnOff at all: if the only initial setting and all updates ONLY come from its parent component, then a stateless component would be better.
One of the important things to understand when "Thinking in React" is to figure out which component does State belong to.
Read this in React docs
What Components Should Have State?
Most of your components should simply take some data from props and render it. However, sometimes you
need to respond to user input, a server request or the passage of
time. For this you use state.
Try to keep as many of your components as possible stateless. By doing
this you'll isolate the state to its most logical place and minimize
redundancy, making it easier to reason about your application.
A common pattern is to create several stateless components that just
render data, and have a stateful component above them in the hierarchy
that passes its state to its children via props. The stateful
component encapsulates all of the interaction logic, while the
stateless components take care of rendering data in a declarative way.
Thus, your OnOff should not have state but use properties passed down from the parent instead. I have illustrated this at http://codepen.io/anon/pen/gaxbGm?editors=101
render() {
writeLog("OnOff render called!")
writeLog("Child: " + this.props.initialOn)
return <span>{this.props.initialOn ? "On" : "Off"}</span>;
}
I would also recommend reading "Thinking in React" to get further clarity.

Resources