Redux mapStateToProps called multiple times - reactjs

I have this very simple Component, which is connected to redux state and returns {fruit, vegetables}. Everything works fine, but let's say I have a graph inside the Component and I if receive only updated vegetable from API the graph is being recreated each time.
Here's my component:
const Products = ({ fruit, vegetable }) =>
<div className='Products'>
<div>{vegetable.map(....logic here)}</div>
<div>{Math.random()}</div> // this is to illustrate the component is rendering every time
<Graph>Here will be a chart or a graph with fruit</Graph> //but it gets re-rendered even though there is no new fruit
</div>
const mapStateToProps = (state) => {
return {
fruit: state.data.fruit,
vegetable: state.data.vegetable,
}
}
export default connect(mapStateToProps)(Products)
It seems to me that every-time, no matter which states is updated it re-renders the whole components.
Is there a way to prevent that?

When a React component gets rendered, the whole tree of components below it also gets rendered - at the exception of the components which shouldComponentUpdate hook returns false. So in your case, if the Products component gets rendered, it is normal that the Graph component also does.
You have two options here:
if your Products component does not use the fruit prop outside of the Graph component, you can connect directly your Graph component to the fruitstate, and use the pure option of the connect function to avoid re-renders when fruit does not change
you can define the shouldComponentUpdate hook in your Graph component to manually skip unnecessary renders, or use a helper library to do it for you, for example the pure helper of the recompose library
The first option is where optimizing react/redux apps / avoiding unnecessary renders generally starts: connect your components to the store at the lowest level where it makes sense. The second option is more of an escape hatch - but still often useful.
As you mention you use stateless components, you can use a higher-order component to benefit from the shouldComponentUpdate hook. To understand how this works, here's how a simple implementation of it could look like this:
function pure(BaseComponent, shouldUpdateFn) {
return class extends Component {
shouldComponentUpdate(nextProps) {
return shouldUpdateFn(this.props, nextProps);
}
render() {
return <BaseComponent { ...this.props } />;
}
}
}
This would give you a pure HOC that you could reuse over your app to avoid unnecessary renders: it works by wrapping your stateless component into a new component with the desired hook. You'd use it like so, for example:
export default pure(Graph, (props, nextProps) => props.fruit !== nextProps.fruit)
Still, i highly encourage you in having a look at recompose, which has more fine-grained implementations of this, and would avoid you to reinvent the wheel.

To prevent a component to rerender when receiving new props, you can implement shouldcomponentupdate() in Graph.
Use shouldComponentUpdate() to let React know if a component's output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority of cases you should rely on the default behavior.
shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate() is used.
Returning false does not prevent child components from re-rendering when their state changes.
Currently, if shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked. Note that in the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component.
If you determine a specific component is slow after profiling, you may change it to inherit from React.PureComponent which implements shouldComponentUpdate() with a shallow prop and state comparison. If you are confident you want to write it by hand, you may compare this.props with nextProps and this.state with nextState and return false to tell React the update can be skipped.
To help you implementing shouldComponentUpdate(), you can use eitherReact shallow-compare() or a custom shallow compare function

Given your current code.
React will update the whole component when state is changed.
So Graph Component will get updated.
If you don't want Graph Component to get updated you can add shouldComponentUpdate in your Graph Component and introduce checks there for re-rendering like as follows
shouldComponentUpdate: function(nextProps, nextState) {
// You can access `this.props` and `this.state` here
// and check them against nextProps and nextState respectively.
// return boolean(false) if you don't want the component to re-render.
}

Related

Is this.setState({}) guaranteed to cause a re-render

I inherited some code to maintain, with this line in it:
this.setState({}); // Force update
Is this guaranteed to cause a re-render?
setState merges the object passed as argument into the actual state in an immutable way. this.setState({}) will merge nothing to state but will actually return a new object, the shallow comparison performed by React will always assert to false and a re render will be triggered, unless explicitly cancelled with shouldComponentUpdate. So yes, in this case it is equivalent to forceUpdate and it comes with the same caveats.
You can actually test it easily:
import React, { Component } from 'react';
import { Button } from 'react-native';
class Test extends Component {
render() {
console.log('render');
return <Button onPress={() => this.setState({})} title='Test' />;
}
}
export default Test;
Every time the button is clicked, the console.log triggers.
An interesting point is that if you replace this.setState({}) by this.setState(), there is no re-render after a click.
According to the documentation it is and several other methods in order
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
please see this link for detailed
It depends. If you want to render a component, react internally checks is DOM equals with previous(this occurs if props of component is not changed at all). If dom equals with previous version, react checks shouldComponentUpdate. forceUpdate is different than this.setState({}), which always render components.

`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 component does not re-render, but render() gets called

This is my component. It renders the first time. Subsequently, even though render gets called, it does not change the DOM.
Render is getting called at the right times, because I have redux set up and my 'mapStateToProps' seems to work correctly, in that it detects a change in application state, and calls render().
The console log does log the fact that the string has changed.
You would probably like to see more code, but I'm hoping that I'm missing a fundamental concept here that someone can just point out. I'm not sure how to put my whole project up here. Thanks. Again, render DOES get called, so shouldn't it update the DOM?
import React from 'react';
import { Component } from 'react';
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
class TinyMCETestResultElement extends Component {
render (){
console.log ("this.props.form0DataToDisplay " + this.props.form0DataToDisplay)// logs a changed string, as expected
return (
<div>
<div>results: {this.props.form0DataToDisplay}</div>
</div>
)
}
}
function mapStateToProps(state){
//whatever gets returned will show up as props inside of
return{
form0DataToDisplay: state.tinyMceTestData
}
}
export default connect( mapStateToProps, null)(TinyMCETestResultElement )
Math.random() in render block is the reason of such components behaviour.
React will rerender component only in case of it's state or props was changed. There is no updated props/state in your code sample, so component rendered only ones.
You can create variable in store for random value and update it with Math.random() into reducer. Then use it in your component as props and all wonna be ok.
It's probably because Redux's connected component is implementing shouldComponentUpdate for you, and it sees that your props don't change. As a result it won't update that component for you.
You can read about it here:
Redux Docs: http://redux.js.org/docs/basics/UsageWithReact.html
"...we suggest instead generating container components with the React Redux library’s connect() function, which provides many useful optimizations to prevent unnecessary re-renders. (One result of this is that you shouldn’t have to worry about the React performance suggestion of implementing shouldComponentUpdate yourself.)"
Twitter: https://twitter.com/dan_abramov/status/719723707124604928
A quick fix would probably be to implement shouldComponentUpdate to always return true, overriding Redux's implementation. Or better, having your props change everytime you get a new random number.
I should have mentioned that I was using a tiny mce component as the source of the change in state. If anyone comes across this question, there seemed to be an issue with loading that component, which could be fixed by re-rendering after initial load. I added this code to the tiny mce component to fix it. Timer may not be necessary.
componentDidMount(){
var that = this;
setTimeout(function(){
that.forceUpdate();
}, 1)
}

Should updating props re-render the entire component?

Let's say I have a CookingClass component that gets initialized like this.
let teachers = makeTeachers(["Amber", "Jason", "Lily"])
let students = makeStudents(["Hopper"])
<CookingClass
teachers={teachers}
students={students}
/>
One of the teachers dropped out:
let newTeachers = makeTeachers(["Amber", "Jason"])
<CookingClass
teachers={newTeachers}
/>
It will make the entire component re-render. I am not sure whether React only calculates the diff and efficiently re-renders it or I must use shouldComponentUpdate to take care of it myself.
More real-world example might be implementing a Google map where there are a million markers and you want to replace one of the markers.
You're changing a so called Virtual DOM node. For every change in a virtual node shouldComponentUpdate() gets called. If you don't implement it yourself it will always return true;
So if you only want to reload your CookingClass in specific cases you would have to implement it yourself.
The pro of React is that it will only re-render Native DOM nodes when they will get changed in the Virtual DOM. This is the "render" which makes React so fast.
Based on your sample code, the component will re-render everytime.
You should use the react-redux (documentation) bindings to "connect" the component to the store.
// ConnectedCookingClass.js
import { connect } from 'react-redux';
import CookingClass from './CookingClass';
const mapStateToProps = (state) => {
return {
teachers: state.teachers,
students: state.students
};
};
const ConnectedCookingClass = connect(mapStateToProps)(CookingClass);
export default ConnectedCookingClass;
Then use this component elsewhere like so:
// OtherComponent.js
import ConnectedCookingClass from './ConnectedCookingClass';
const OtherComponent = React.createElement({
render() {
return (
<div>
<ConnectedCookingClass />
</div>
);
}
});
The react-redux bindings will do some smart things for you, like only re-rendering the component when the props returned by mapStateToProps are actually different than their previous value (via a shallowEqual comparison), so you should try to only return values here, no functions. Functions should be returned in mapDispatchToProps.
The default implementation of shouldComponentUpdate in react-redux will return true when:
ALWAYS if the component is a "pure" component (aka stateless-function)
When the props have been updated manually (after componentWillReceiveProps called)
When the store has changed and the new props are different than the old props.
Here's what that looks like from the source code:
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}
The real DOM Rendering is completely handled by React with very efficient innerHTML inserts and only for changes in the new data structure of your application VirtualDomTree.
shouldComponentUpdate() controls if the component should be recalculated or not. You should use it, when you are rendering statical data, for example. The output of the component will not change, so you could just return false and the first version of the component will be used for ever ;)

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