I really don't get the difference between render and component prop in Route in react router, in docs it says that render doesn't create new element but component does, I tried to go back with history but I found componentWillMount is called when I use render in Route, what do they mean by "if you provide an inline function to the component attribute, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component."
render makes the component mount just once, then re-render when required. The component stays in the background — this means that anything you put in componentDidMount , constructor , or, for example, shouldComponentUpdate , will run only once! Also, because the component doesn't unmount, you may run into data leakage.
Route render methods These are provided mostly for supporting apps that were built with earlier versions of the router before hooks were introduced. You should use only one of these props on a given <Route> . See their explanations below to understand the differences between them.
Link and Route Components The <Route> component is the most important component in React Router. It renders some UI if the current location matches the route's path. Ideally, a <Route> component should have a prop named path , and if the path name matches the current location, it gets rendered.
React components don't have to render anything.
The source code tells the difference:
if (component) return match ? React.createElement(component, props) : null if (render) return match ? render(props) : null
When you use component
prop, the component is instantiated per every call of Route#render
. It means that, for your component that you pass to component
prop of Route, constructor, componentWillMount
, and componentDidMount
will be executed every time the route is rendered.
For example, if you have
<Route path="/:locale/store" component={Store} />
and the user navigates to /en/store, then goes elsewhere, and then navigates back to /en/store, the Store component will be mounted, then unmounted, and then mounted again. It is similar to doing
<Route path="/:locale/store"> <Store /> </Route>
Compared to that, if you use render
prop, the component is evaluated on every Route#render
. Remember that every component is a function? This function will be executed as is, without any lifecycle methods. So when you have it like
<Route path="/:locale/store" render={Store} />
you can think of it as
<Route path="/:locale/store"> {Store()} </Route>
It saves you runtime because no lifecycle methods are run, but it also has a downside in case Store component has some post-mount lifecycle methods like shouldComponentUpdate that may increase performance as well.
There was a good post on Medium about this performance hack, please take a look at it. It's written very well and is applicable to React 16, too.
So I'm confused on this section of docs as well, but I finally figure it out.
The key to understand this is the statement "provide an inline function to the component prop"
We all know that Route component will re-render when the location changed, and react will compare the old and new virtual DOM tree, get some diff result and apply to the real DOM.
And react will try it's best to reuse the DOM node, unless the type or key prop of the new ReactElement is changed.
So
// 1. const componentA = React.createElement(App, props) const componentB = React.createElement(App, props) console.log(componentA.type === componentB.type) // true // 2. const componentA = React.createElement(() => <App />, props) const componentB = React.createElement(() => <App />, props) console.log(componentA.type === componentB.type) // false
All ReactElements created by way 1 have the same type(App component), but they don't have the same type if they are all created by way 2.
Why?
Because there is always a new anonymous function created in the way 2 when the parent component's(The component that contains Route component) render method got invoked, so the type of new&old ReactElement is two different instances of the anonymous function
() => <App />
So in React's point of view, there are different types element and should be treat with unmount old > mount new operation, that means every state or changes you made on the old component got loss everytime the parent component re-render.
But why the render prop avoid the unmount and mount behavior? It's an anonymous function too!?
Here I would like to refer the code that @Rishat Muhametshin posted, the core part of Route component's render method:
if (component) // We already know the differences: // React.createElement(component) // React.createElement(() => <component/>) return match ? React.createElement(component, props) : null if (render) return match ? render(props) : null
render prop is a function that return a ReactElement when invoked, what's the type of that returned element?
<Route render={() => <AppComponent />}></Route>
It's AppComponent, not the anonymous function wrapper! Because after jsx compiled:
render = () => React.createElement(AppComponent) render() = React.createElement(AppComponent) React.createElement(render) = React.createElement(() => React.createElement(AppComponent)) React.createElement(render()) = React.createElement(React.createElement(AppComponent))
So when you use render instead of component prop, the type of element that render prop function return will not change on each render, even there always an new anonymous function instance created on each parentElement.render()
On my point of view, you can achieve the same behavior that render prop does with component prop by giving a name to the anonymous function:
// Put this line outside render method. const CreateAppComponent = () => <AppComponent /> // Inside render method render(){ return <Route component={CreateAppComponent}/> }
So the conclusion is, there is not performance different between component and render prop if you are use component={AppComponent} directly, if you want to assign some props to AppComponent, use render={() => <AppComponent {...props}/> }
instead of component={() => <AppComponent {...props}/> }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With