在react router官方文檔關於component的部分寫着:
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).
這段話如何理解呢?可以通過一個小例子來說明。
import React from 'react'; import ReactDOM from 'react-dom'; import {BrowserRouter, Route} from "react-router-dom"; class Bar extends React.Component { componentDidMount() { console.log("componentDidMount") } render() { return ( <div>Bar</div> ) } } class App extends React.Component { constructor(prop) { super(prop); this.state = {idx: 1} } handleClick = () => { this.setState(state => ({idx: state.idx + 1})) }; render() { return ( <div> <div> {this.state.idx} <button onClick={this.handleClick}>add</button> </div> <div> <BrowserRouter> <Route component={Bar}/> </BrowserRouter> </div> </div> ); } } ReactDOM.render(<App/>, document.getElementById('root'));
上面的代碼中,App組件內有一個簡單的Bar組件,通過component屬性被Route引用。
<Route component={Bar}/>
此時在頁面中點擊按鈕,Bar組件的componentDidMount並不會被觸發。
假設現在需要在Bar組件中接受App中的idx,需要將idx作為props傳遞給Bar,此時可以寫成如下代碼
import React from 'react'; import ReactDOM from 'react-dom'; import {BrowserRouter, Route} from "react-router-dom"; class Bar extends React.Component { componentDidMount() { console.log("componentDidMount") } render() { const {idx} = this.props; return ( <div>in Bar : {idx}</div> ) } } class App extends React.Component { constructor(prop) { super(prop); this.state = {idx: 1} } handleClick = () => { this.setState(state => ({idx: state.idx + 1})) }; render() { return ( <div> <div> {this.state.idx} <button onClick={this.handleClick}>add</button> </div> <div> <BrowserRouter> <Route component={() => (<Bar idx={this.state.idx}/>)}/> </BrowserRouter> </div> </div> ); } } ReactDOM.render(<App/>, document.getElementById('root'));
然而此時點擊按鈕,發現Bar的componentDidMount一直被調用,就像上面文檔中說的一樣
That means if you provide an inline function to the component 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.
所以正確的寫法應該是
<Route render={() => (<Bar idx={this.state.idx}/>)}/>
此時Bar組件就不會出現重復的unmounting和mounting了。
其背后的原理在於,react在比較組件狀態以便決定如何更新dom節點時,首先要比較組件的type和key。在使用<Route component={() => (<Bar idx={this.state.idx}/>)}/>
時,由於調用了React.createElement
,組件的type不是Bar這個類,而是一個匿名函數。App組件每次render時都生成一個新的匿名函數,導致生成的組件的type總是不相同,所以會產生重復的unmounting和mounting。
參考文檔:
https://reactjs.org/docs/reconciliation.html
https://reactjs.org/docs/react-api.html#createelement
作者:大神帶我來搬磚
鏈接:https://www.jianshu.com/p/a2a9b469a422
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
喜歡這篇文章?歡迎打賞~~