聊一聊 React 中更新 ui 檢視的幾種方式

虹晨發表於2018-10-07

前言

剛為祖國母親慶完生,眼看假餘額就要不足了,小夥伴們玩的是否開心呢,反正我是死宅在家,沒出去玩,在家也沒好好學習,實屬慚愧。這不,今天和大家分享下關於 react 驅動 ui 更新的幾種方式,都說 react 是單向資料流,資料驅動 ui,那麼你知道在 react 中有幾種驅動檢視更新的方式呢。

1. setState

setState 是眾所周知最常見的更新檢視的方式了,只需要給個初始 state,需要更新的時候呼叫 this.setState, 元件會經歷 shoudlComponentUpdate => componentWillUpdate => render => componentDidUpdate 四個過程,如果沒有在 shouldComponentUpdate 中手動 return false 的情況下,那麼 ui 此時就會更新。

需要注意的是,儘管 this.setState({}) 引數為空物件時,react 一樣會更新,呼叫上面的四個生命週期,只是 ui 檢視不有會變化。

當然這些都要基於 class 元件,因為 setState 是 Component 原型上的方法,必須得是繼承 Component的元件才能呼叫 this.setState。(之前有寫過一篇關於setState的文章,感興趣的大佬也可以去看看。)

2. forceUpdate

Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate(). This will trigger the normal lifecycle methods for child components, including the shouldComponentUpdate() method of each child. React will still only update the DOM if the markup changes.

官方說的比較清楚,呼叫 forceUpdate 後當前元件會跳過 shouldComponentUpdate 這個鉤子,儘管手動 return false,也一樣會更新,單詞字面理解也是強制更新,但是需要注意的是,子元件的更新還是會受到 shouldComponentUpdate 控制。

Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().

forceUpdate的使用場景一般是檢視更新來源於其他非 state、props的資料,比如繫結在 元件例項上的屬性,或者是直接修改 this.state.xxx = xxx ,然後呼叫 this.forceUpdate(),不過官方不建議這麼做,正常情況下應該避免使用 forceUpdate,通過 state 或者 props 去更新檢視。

3. 原生操作dom

在 react 中難免會有操作原生 dom 的時候,比如你用了第三方比如 jquery 這種需要獲取 dom 的庫,或者你需要實現一個拖拽,雙指縮放的元件,對於這些,你也許可以用操作的 dom 的方式繞過 react 的 setState 再到 dom diff 一系列的計算,直接更新 dom ,提高些許效能。

以上三種更新 ui 的方式,我這邊有個Demo, 需要注意的是,當通過 setState 更新改變顏色變紅,在點選原生 dom 按鈕改變顏色變綠,這時在點選 setState 的按鈕,發現檢視不更新,但是還是走了 render 函式,這是因為點選原生 dom 按鈕前, this.state.backgroundColor 值是 red,原生操作是直接改變的 dom style,在點回 setState 按鈕,其實 this.state.backgroundColor的值還是 red, 雖然走了更新邏輯,但是由於 react 的 新老 virtual dom 對比的時候,發現顏色並沒改變, 導致沒有 patch 到 ui 上

4. dispatch action

上面的幾種方式都是通過 react 本身自帶的 state 去更新 ui, 當專案中使用 redux 時, 我們一般會通過 dispach 一個 action, 改變 store,然後更新 ui,dispatch action 是通過改變 props 去驅動檢視的,大家在使用的時候有沒有想過為什麼 this.props.dispatch({ type: 'xxx' }), 就可以驅動 ui 檢視更新呢?

這邊簡單的講下,當我們dispatch 一個 action 的時候, 呼叫的其實是 store.dispatch,這個都沒問題,store.dispatch 會去跑一遍所有註冊在 createStore 中的 reducer, 找到對應的 type 更新資料,返回一個新的 state。

而我們的元件想拿到 store 的資料必須通過 connect(mapStateToProps, mapDispatchToProps)(App) 像這樣,react-redux 中的 Connect 元件會在 componengDidMount 的時候去 呼叫 一個 trySubscribe 的方法,其內部呼叫 store.subscribe 去訂閱一個 handleChange 的方法。

所以當你 dispatch action 的時候,就會觸發 Connect 元件中的方法, Connect 元件中也維護了一個叫做 storeState 的 state,每次拿到新的 sotre 就去呼叫 setState, 觸發 render 函式, render 函式會根據你 connect 中傳入的 mapStateToProps, mapDispatchToProps,包括可選引數 mergeProps, 去做一個 props 的合併動作, 最終在 Connect 元件內部 return 出來一個 createElement(WrappedComponent,this.mergedProps) 這樣的東西,而 createElement 第二個引數就是你元件的 props, 那麼每次 props 變了,就會驅動檢視的更新。這就是 Redux 其中的中做原理,當然這邊描述的比較簡短, 下次單獨寫一篇關於 react、 redux、 react-redux 的文章。

總結

  1. 通過呼叫 this.setState 改變 state 驅動檢視。
  2. 通過呼叫 this.forceUpdate 強制更新檢視。
  3. 通過操作原生 dom 更新檢視。
  4. 通過改變 props 驅動檢視(redux 或者 修改父子元件傳遞 props )。

相關文章