拆解高頻面試題:你是如何理解單向數據流的?
本文轉載自微信公眾號「勾勾的前端世界」,作者西嶺 。轉載本文請聯系勾勾的前端世界公眾號。
今天的主要內容是組件狀態。
狀態可以簡單的理解為數據,與 props 類似,但是 state 是私有的,并且完全受控于當前組件,因此組件狀態指的就是一個組件自己維護的數據。
上篇我們也提到了一個非常重要的點:數據驅動UI。意思也很簡單,就是頁面所展示的內容,完全是受狀態控制的,這也就是所謂 MVVM 的理念。UI 的改變,全部交給框架本身來做,我們只需要管理好 “數據(狀態)” 就可以了。
那么在 React 中,如何對狀態進行管理呢?這就是本章節的重點,也是整個 React 學習的重點:組件的狀態管理。
基本使用
state 的使用是非常簡單的,我們在類中聲明一個名為 state 的對象,對象中的元素就是當前組件所維護的狀態數據,獲取展示數據時,只需要在 jsx 中,使用 this.state.xx 的方式獲取就可以了。
- import React, { Component }from'react'
- exportclass States extends Component {
- // 聲明 state 對象
- state = {
- name:'xiling',
- age:18
- }
- render() {
- return (
- <>
- <h2>state 狀態</h2>
- {/* 使用 this.state.xx 獲取數據 */}
- <p>{this.state.name}</p>
- <p>{this.state.age}</p>
- </>
- )
- }
- }
- exportdefault States
前面我們說,state 數據是可以控制界面的,那么我們如何修改 state 從而讓界面發生改變呢?
修改狀態
想要修改 state 的值,最直觀的方式就是直接使用 this.state={} 的方式直接修改。我們設置一個按鈕,當點擊按鈕時,通過 this.state={} 發現是不起作用的,那應該怎么做呢?
React 給我們提供了專門的 this.setState({}) 方法,我們需要調用 this.setState({}) 方法將需要修改的數據傳入才能正確的修改 state 的值。
至于為什么,需要我們理解 React 數據流才能搞懂,這里就不再詳細介紹,你只需要記住這個規則就可以了。
- import React, { Component }from'react'
- exportclass States extends Component {
- // 聲明 state 對象
- state = {
- name:'xiling',
- age:18
- }
- // 箭頭函數
- changes = ()=>{
- // console.log(22)
- // this.state.name = 'xiling' // 錯誤的使用方式
- this.setState({name:'西嶺'})
- }
- render() {
- return (
- <>
- <h2>state 狀態</h2>
- {/* 使用 this.state.xx 獲取數據 */}
- <p>{this.state.name}</p>
- <p>{this.state.age}</p>
- <buttononClick={this.changes}>改變state</button>
- </>
- )
- }
- }
- exportdefault States
一旦 state 的值發生了改變,那么 JSX 中使用 state 的地方就會自動發生改變。
這里也需要注意一點,因為 setState 方法是類中的屬性(方法),我們需要使用 this 進行獲取,因此,事件綁定的處理函數就需要使用箭頭函數來固定 this 的指向,一定不要使用普通的函數 (類方法) 聲明,否則會因為找不到方法而直接報錯。
自頂向下的單向數據流
關于數據流的問題,是面試中高頻次出現的典型題目,一般情況下面試官會直接問:“你是如何理解單向數據流的 ? ”。
注意,這不是一個單獨的個體問題,而是數據流問題的綜合體。解答這個問題,你需要解釋:
什么是數據流?
為什么是自頂向下的?
單向數據流是什么意思?
為什么是單向的?不能是雙向的數據流嘛?
單向數據流有什么作用呢?
面試題一旦拆開,你會發現面試官問出來的幾乎每一個詞都需要解釋。寶兒,這個問題,真不簡單啊!
那么,我應該怎么解答呢?
說實話,并沒有標準答案,因為數據流這個問題,涉及到了框架本身的設計理念,需要你對框架的設計有深入理解,你要站在框架作者的角度看待問題;但是,對于初學者來說,這個問題顯然超綱了。
完犢子,那么重要,我又學不了是嘛?不是,你需要學很多遍,這只是第一遍。
開始之前,我們先來看一段普通的 JS 代碼:
- var datas = {
- name:'lisi',
- age:18
- }
- var l1 = datas
- var l2 = l1
- var l3 = l2
- l1.age=20
- console.log(l1.age,l2.age,l3.age) // 20 20 20
- l3.age=26
- console.log(l1.age,l2.age,l3.age) // 26 26 26
你會發現,無論我們是修改那個變量的 age 屬性,其他數據都會跟著改變,原因也很簡單,大家都是共享一個內存數據的。
但是,賦值的前后邏輯上,我們可以將 L3 節點看作孫子,L2 節點看做父親,L1 節點看做爺爺。
任意一個節點的數據改變之后,所有節點的數據都會跟著改變,我們就可以把這種現象看做是數據在“變量節點”上的流動。
但是,這樣的數據流動,是雙向的,拿 L2這個節點來說,只要數據改變,上層的 L1 節點和下層的 L3 節點都會跟著改變。
雖然這個例子并不恰當,但是回到 React 組件中,道理是一樣的,所謂數據的流動就是數據在組件間的傳遞。前面我們用了很大的篇幅講解的組件間的值傳遞,其實就是在講數據流這個概念的具體用法。
那么,我們在數據流前面加上一個“單向”的定語,叫 “單向數據流” 是什么意思呢?其實現在你理解起來很簡單,就是數據在某個節點被改變后,只會影響一個方向上的其他節點。
那所謂的自頂向下又怎么解釋呢?
更簡單了,就是數據只會影響到下一個層級的節點,不會影響上一個層級的節點。用上面的例子解釋,就是如果 L2 數據改變,只會影響到 L3,不會影響到 L1 或者其他節點。
這就是 “自頂向下的單向數據流”。那么我們在 React 框架中,就可以明確定義單向數據流:規范數據的流向,數據由外層組件向內層組件進行傳遞和更新。
那么,在具體的代碼實現中,是怎么體現出來的呢?翠花,上代碼:
圖有點看不清,接下來,我們看具體代碼的演示:
- // ========== App============
- import React, { Component } from'react'
- import C1 from'./C1'
- exportclass App extends Component {
- state = {
- name:"xiling"
- }
- render() {
- return (
- <div>
- <h1>App</h1>
- <p> APP 中的值:
- <bstyle={{ color:"red" }}>
- {this.state.name}
- </b>
- </p>
- <C1toC1={this.state.name}></C1>
- </div>
- )
- }
- }
- exportdefault App
- // ========== C1 ============
- import React, { Component } from'react'
- import C2 from'./C2'
- exportclass C1 extends Component {
- render() {
- return (
- <div>
- <h2>C1</h2>
- <p>傳入C1 的值(App傳入):
- <bstyle={{ color:"red" }}>
- {this.props.toC1}
- </b>
- </p>
- <C2toC2={this.props.toC1}></C2>
- </div>
- )
- }
- }
- exportdefault C1
- // ========== C2 ============
- import React, { Component } from'react'
- import C3 from'./C3'
- exportclass C2 extends Component {
- state = {
- name:this.props.toC2
- }
- changes = () => {
- this.setState({
- name:Math.random()
- })
- }
- render() {
- return (
- <div>
- <h2>C2</h2>
- <buttononClick={() => { this.changes() }}>
- 修改
- </button>
- <p>傳入C2 的值(C1傳入):
- <bstyle={{ color:"red" }}>
- {this.state.name}
- </b>
- </p>
- <C3toC3={this.state.name}></C3>
- </div>
- )
- }
- }
- exportdefault C2
- // ========== C3 ============
- import React, { Component } from'react'
- exportclass C3 extends Component {
- render() {
- return (
- <div>
- <h2>C3</h2>
- 傳入C3 的值(C2傳入):
- <bstyle={{ color:"red" }}>
- {this.props.toC3}
- </b>
- </div>
- )
- }
- }
- exportdefault C3
最后,我們再來解釋,為什么?有什么用?
其實這才是這個問題的核心,不同的技術理解,就會有不同的角度解釋,我這里僅一家之言,你且聽聽罷。
我們設想這樣的情景:
父組件的數據通過props傳遞給子組件,而子組件里更新了 props,導致父組件和其他關聯組件的數據更新,UI 渲染也會隨數據而更新。
毫無疑問,這是會導致嚴重的數據紊亂和不可控。
因此絕大多數框架在這方面做了處理。而 React 在這方面的處理,就是直接規定了 Props 為只讀的,而不是可更改的。這也就是我們前面看到的數據更新不能直接通過 this.state 操作,想要更新,就需要通過 React 提供的專門的 this.setState() 方法來做。
單向數據流其實就是一種框架本身對數據流向的限制。
暫時先說這些吧,等我們學的越多,經驗越豐富,對它的理解也就會越深刻,看待它的角度也就越全面。