Difference between render and component prop on React Router v4
So recently I’m reading the docs of React-Router, and I’m quite confuse about Route component’s render and component prop, the doc say for component prop:
When you use
component
(instead ofrender
orchildren
, below) the router usesReact.createElement
to create a new React element from the given component. That means if you provide an inline function to thecomponent
prop, 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. When using an inline function for inline rendering, use therender
or thechildren
prop (below).
Quite confuse isn’t it? 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(AppComponent, props)
const componentB = React.createElement(AppComponent, props)
console.log(componentA.type === componentB.type) // true// 2.
const componentA = React.createElement(() => <AppComponent />, props)
const componentB = React.createElement(() => <AppComponent />, props)
console.log(componentA.type === componentB.type) // false
All ReactElements created by way 1 have the same type(AppComponent), 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
() => <AppComponent />
So in React’s point of view, there are different types of element and should be treat with unmount old > mount new operation when an re-render happen, 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 can avoid the unmount and mount behavior? It’s an anonymous function too!?
Here I would like to refer some source code of Route component’s render method:
if (component)
// We already know the differences for these two:
// React.createElement(AppComponent)
// React.createElement(() => <AppComponent />)
return match ? React.createElement(component, props) : nullif (render)
return match ? render(props) : null
render prop is a function that return an ReactElement when invoked, now stop and spent few secs to think about - what’s the type of that returned element?
<Route render={() => <AppComponent />}></Route>
It’s AppComponent, not the anonymous function wrapper! Because after jsx compiled we got:
if
render = () => React.createElement(AppComponent)then
render() = React.createElement(AppComponent)if
React.createElement(render) =
React.createElement(() => React.createElement(AppComponent))then
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() call.
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 and put it outside the parent’s render method:
// 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}/> }