Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent react component from rendering twice when using redux with componentWillMount

Tags:

reactjs

redux

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

like image 352
Luke Sapan Avatar asked Dec 18 '15 02:12

Luke Sapan


People also ask

How would you prevent a component from rendering in React?

memo() If you're using a React class component you can use the shouldComponentUpdate method or a React. PureComponent class extension to prevent a component from re-rendering.

Why does React render multiple times?

You can see in the console tab, that the render lifecycle got triggered more than once on both the app and greeting component. This is because the React app component got re-rendered after the state values were modified, and it also re-rendered its child components.

Does componentWillMount run before render?

Using componentWillMount() to Manipulate State As you know, the life-cycle hook componentWillMount triggers before the initial render, and the function will only trigger once in the lifespan of a component. It is used to update the state value before the DOM is rendered, creating a state variable, as shown below.


Video Answer


2 Answers

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.

like image 109
bryanph Avatar answered Sep 25 '22 21:09

bryanph


It looks like there's already an issue in the react-redux library.

https://github.com/rackt/react-redux/issues/210

like image 29
Luke Sapan Avatar answered Sep 24 '22 21:09

Luke Sapan