Should updating props re-render the entire component? - reactjs

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 ;)

Related

Redux mapStateToProps called multiple times

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.
}

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)
}

Limit Redux to update only components affected by the change

trying to understand React-Redux, i find it unusual that all my components get new props when ever any slice of the state get changed. so is this by design or i'm doing something wrong ?
example App
class App extends React.Component {
render(){return (
<div>
<Navbar data={this.props.navbar} />
<Content data={this.props.content} />
</div>);
}
}
select (state) => ({ navbar:state.navbar, content:state.content});
export default connect(select)(App);
Components
export const NavbarForm = props => {
console.log('RENDERING with props--->',props);
return (<h1>NAV {props.data.val}</h1>);
};
export const ContentForm = props => {
console.log('RENDERING CONTENT with props--->',props);
return (<h1>CONTENT {props.data.val}</h1>);
};
////////INDEX.js//////
const placeholderReducer = (state={val:0},action)=>{
//will update val to current time if action start with test/;
if(action.type.indexOf('TEST/') === 0)return {val:Date.now();}
return state;
}
export const rootReducer = combineReducers({
navbar:placeholderReducer,
content: (state,action)=>(state || {}), //**this will never do a thing.. so content should never updates right !!**
});
const store = createStore(rootReducer, {}, applyMiddleware(thunk));
render( <Provider store={store}> <App /></Provider>, document.getElementById('app')
);
setInterval(()=>{ store.dispatch(()=>{type:'TEST/BOOM'}) },3000);
okay in this app, what i expect is that Navbar component will get updated every 3000ms while content component will never updates because its reducer will always return the same state.
yet i find it really strange that both components does reRender every time an action is fired.
is this by design ? should i worry about performance if my app has 100+ component ?
This is entirely by design. React assumes that your entire app will be re-rendered from the top down by default, or at least a given subtree will be re-rendered if a certain component does a setState or something similar.
Because you only have the very top component in your app connected, everything from there on down is React's standard behavior. A parent component re-renders, causing all of its children to re-render, causing all of their children to re-render, and so on down.
The core approach to improving UI performance in React is to use the shouldComponentUpdate lifecycle method to check incoming props and return false if the component does not need to re-render. This will cause React to skip re-rendering that component and all of its descendants. Comparisons in shouldComponentUpdate are generally done using shallow reference equality, which is where the "same object references means don't update" thing becomes useful.
When using Redux and connect, you will almost always find yourself using connect on many different components in your UI. This provides a number of benefits. Components can individually extract the pieces of the store state that they need, rather than having to hand them all down from the root component. In addition, connect implements a default shouldComponentUpdate for you, and does a similar check on the values you return from your mapStateToProps function. So, in a sense, using connect on multiple components tends to give you a "free win" in regards to performance.
Further reading on the topic:
Redux FAQ: Connecting multiple components
React/Redux Links: Performance articles
Yes this is by design. Action is dispatched. Reducers run. Store subscribers get notified "the store has changed". Connected components are store subscribers.
Typically you just don't worry about it until you can actually measure a performance problem that you can attribute to this - don't prematurely optimize.
If you find out that it is a problem, then you can do one of the following:
Add a shouldComponentUpdate method to your components so they can see that the props they received aren't different and do not need to render (there are lots of Pure Render mixins & high order components available to make this easy)
Instead of connecting the top-level app, connect the Navbar and Content components directly. The App will never rerender, but the children will if the store changes. And react-redux automatically uses shouldComponentUpdate to only re-render the connected components that actually have new props.

Prevent react component from rendering twice when using redux with componentWillMount

I have a React component that dispatches a redux state change in its componentWillMount function. The reason is that when the component is loaded, it needs to get the id from the url (powered by react-router), and trigger an action that sets up the state with that id's data.
Here is the component:
class Editor extends React.Component {
componentWillMount() {
const { dispatch, params } = this.props
dispatch(editItem(params.id))
}
render() {
const item = this.props.item
console.log("Editing", item)
}
}
export default connect(state => ({item: state.item}))(Editor)
Here's the catch: render is getting called twice. item is undefined on the first call, and valid on the second. Ideally, it should only be called once this.props.item actually exists (after the editItem action has been dispatched and run).
According to the React docs: "If you call setState within this method, render() will see the updated state and will be executed only once despite the state change."
In redux, dispatch is the equivalent of calling setState, as it results in a state change. However, I'm guessing something in the way connect works is still causing render to be called twice.
Is there a way around this besides adding a line like if (!item) return; ?
One thing you might do is create a higher order component that handles the basic pattern of loading a different component (or no component) before the required props are loaded.
export const LoaderWrapper = function(hasLoaded, Component, LoaderComponent, onLoad) {
return props => {
if (hasLoaded(props)) {
return <Component {...props} />
}
else {
if (onLoad) onLoad(props)
return { LoaderComponent ? <LoaderComponent /> : null }
}
}
}
Then you can wrap your component before connecting it to get the desired behaviour.
export default connect(state => ({item: state.item}))(LoaderWrapper(
((props) => !!props.item),
Editor,
null,
(props) => props.dispatch(editItem(props.params.id))
))
You might want to add some currying magic to make sure you can compose these kinds of wrapper functions more nicely. Take a look at recompose for more info.
It looks like there's already an issue in the react-redux library.
https://github.com/rackt/react-redux/issues/210
What does editItem do? Does it add item to the redux state or is it there already?
If it is adding I imagine what is happening is that a render cycle happens with the current props, ie item being blank.
Then it gets rendered again when the props have changed, via setting the item.
One approach to fixing this sort of thing is to create a higher order component that wraps Editor and calls the dispatch action the rendering though is set either to a loading screen or and empty div until item is set. That way you can be assured that Editor will have an item.
But without knowing what editItem does it's sort of hard to know. Maybe you could paste the code for that?

Updating state in more than one component at a time

I have a listview component which consists of a number of child listitem components.
Each child listitem have a showSubMenu boolean state, which display a few extra buttons next to the list item.
This state should update in response to a user event, say, a click on the component DOM node.
childcomponent:
_handleClick() {
... mutate state
this.props.onClick() // call the onClick handler provided by the parent to update the state in parent
}
However, it feels somewhat wrong to update state like, as it mutates state in different places.
The other way i figured i could accomplish it was to call the this.props.onClick directly, and move the child state into the parent as a prop instead, and then do change the state there, and trickle it down as props.
Which, if any, of these approaches is idiomatic or preferable?
First of all, I think that the question's title doesn't describe very well what's your doubt. Is more an issue about where the state should go.
The theory of React says that you should put your state in the higher component that you can find for being the single source of truth for a set of components.
For each piece of state in your application:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy).
Either the common
owner or another component higher up in the hierarchy should own the
state.
If you can't find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
However, a Software Engineer at Facebook said:
We started with large top level components which pull all the data
needed for their children, and pass it down through props. This leads
to a lot of cruft and irrelevant code in the intermediate components.
What we settled on, for the most part, is components declaring and
fetching the data they need themselves...
Sure, is talking about data fetched from stores but what im traying to say is that in some cases the theory is not the best option.
In this case i would say that the showSubMenu state only have sense for the list item to show a couple of buttons so its a good option put that state in the child component. I say is a good option because is a simple solution for a simple problem, the other option that you propose means having something like this:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item} </div>
);
}, this)}
</div>
);
}
});
If, in a future, the list view has to get acknowledge of that state to show something for example, the state should be in the parent component.
However, i think it's a thin line and you can do wathever makes sense in your specific case, I have a very similar case in my app and it's a simple case so i put the state in the child. Tomorrow maybe i must change it and put the state in his parent.
With many components depending on same state and its mutation you will encounter two issues.
They are placed in component tree so far away that your state will have to be stored in a parent component very high up in the render tree.
Placing the state very high far away from children components you will have to pass them down through many components that should not be aware of this state.
THERE ARE TWO SOLUTIONS FOR THIS ISSUE!
Use React.createContext and user context provider to pass the data to child elements.
Use redux, and react-redux libraries to save your state in store and connect it to different components in your app. For your information react-redux library uses React.createContext methods under the hood.
EXAMPLES:
Create Context
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
REDUX AND REACT-REDUX
import { connect } from 'react-redux'
const App = props => {
return <div>{props.user}</div>
}
const mapStateToProps = state => {
return state
}
export default connect(mapStateToProps)(App)
For more information about redux and react-redux check out this link:
https://redux.js.org/recipes/writing-tests#connected-components

Resources