最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React?Context?變遷及背后實現(xiàn)原理詳解

 更新時間:2022年11月07日 09:13:20   作者:冴羽  
這篇文章主要為大家介紹了React?Context?變遷及背后實現(xiàn)原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Context

本篇我們講 Context,Context 可以實現(xiàn)跨組件傳遞數(shù)據(jù),大部分的時候并無需要,但有的時候,比如用戶設(shè)置 了 UI 主題、地區(qū)偏好,如果從頂層一層層往下傳反而有些麻煩,不如直接借助 Context 實現(xiàn)數(shù)據(jù)傳遞。

老的 Context API

基礎(chǔ)示例

在講最新的 API 前,我們先回顧下老的 Context API:

class Child extends React.Component {
  render() {
    // 4. 這里使用 this.context.value 獲取
    return <p>{this.context.value}</p>
  }
}
// 3. 子組件添加 contextTypes 靜態(tài)屬性
Child.contextTypes = {
  value: PropTypes.string
};
class Parent extends React.Component {
  state = {
    value: 'foo'
  }
  // 1. 當 state 或者 props 改變的時候,getChildContext 函數(shù)就會被調(diào)用
  getChildContext() {
    return {value: this.state.value}
  }
  render() {
    return (
      <div>
        <Child />
      </div>
    )
  }
}
// 2. 父組件添加 childContextTypes 靜態(tài)屬性
Parent.childContextTypes = {
  value: PropTypes.string
};

context 中斷問題

對于這個 API,React 官方并不建議使用,對于可能會出現(xiàn)的問題,React 文檔給出的介紹為:

問題是,如果組件提供的一個 context 發(fā)生了變化,而中間父組件的 shouldComponentUpdate 返回 false,那么使用到該值的后代組件不會進行更新。使用了 context 的組件則完全失控,所以基本上沒有辦法能夠可靠的更新 context。

對于這個問題,我們寫個示例代碼:

// 1. Child 組件使用 PureComponent
class Child extends React.Component {
  render() {
    return <GrandChild />
  }
}
class GrandChild extends React.Component {
  render() {
    return <p>{this.context.theme}</p>
  }
}
GrandChild.contextTypes = {
  theme: PropTypes.string
};
class Parent extends React.Component {
  state = {
    theme: 'red'
  }
  getChildContext() {
    return {theme: this.state.theme}
  }
  render() {
    return (
      <div onClick={() => {
        this.setState({
          theme: 'blue'
        })
      }}>
        <Child />
        <Child />
      </div>
    )
  }
}
Parent.childContextTypes = {
  theme: PropTypes.string
};

在這個示例代碼中,當點擊文字 red 的時候,文字并不會修改為 blue,如果我們把 Child 改為 extends Component,則能正常修改

這說明當中間組件的 shouldComponentUpdatetrue 時,會中斷 Context 的傳遞。

PureComponent 的存在是為了減少不必要的渲染,但我們又想 Context 能正常傳遞,哪有辦法可以解決嗎?

既然 PureComponent 的存在導(dǎo)致了 Context 無法再更新,那就干脆不更新了,Context 不更新,GrandChild 就無法更新嗎?

解決方案

方法當然是有的:

// 1. 建立一個訂閱發(fā)布器,當然你也可以稱呼它為依賴注入系統(tǒng)(dependency injection system),簡稱 DI
class Theme {
  constructor(value) {
    this.value = value
    this.subscriptions = []
  }
  setValue(value) {
    this.value = value
    this.subscriptions.forEach(f => f())
  }
  subscribe(f) {
    this.subscriptions.push(f)
  }
}
class Child extends React.PureComponent {
    render() {
        return <GrandChild />
    }
}
class GrandChild extends React.Component {
    componentDidMount() {
      // 4. GrandChild 獲取 store 后,進行訂閱
        this.context.theme.subscribe(() => this.forceUpdate())
    }
    // 5. GrandChild 從 store 中獲取所需要的值
    render() {
        return <p>{this.context.theme.value}</p>
    }
}
GrandChild.contextTypes = {
  theme: PropTypes.object
};
class Parent extends React.Component {
    constructor(p, c) {
      super(p, c)
      // 2. 我們實例化一個 store(想想 redux 的 store),并存到實例屬性中
      this.theme = new Theme('blue')
    }
    // 3. 通過 context 傳遞給 GrandChild 組件
    getChildContext() {
        return {theme: this.theme}
    }
    render() {
        // 6. 通過 store 進行發(fā)布
        return (
            <div onClick={() => {
                this.theme.setValue('red')
            }}>
              <Child />
              <Child />
            </div>
        )
    }
}
Parent.childContextTypes = {
  theme: PropTypes.object
};

為了管理我們的 theme ,我們建立了一個依賴注入系統(tǒng)(DI),并通過 Context 向下傳遞 store,需要用到 store 數(shù)據(jù)的組件進行訂閱,傳入一個 forceUpdate 函數(shù),當 store 進行發(fā)布的時候,依賴 theme 的各個組件執(zhí)行 forceUpdate,由此實現(xiàn)了在 Context 不更新的情況下實現(xiàn)了各個依賴組件的更新。

你可能也發(fā)現(xiàn)了,這有了一點 react-redux 的味道。

當然我們也可以借助 Mobx 來實現(xiàn)并簡化代碼,具體的實現(xiàn)可以參考 Michel Weststrate(Mobx 的作者) 的 How to safely use React context

新的 Context API

基礎(chǔ)示例

想必大家都或多或少的用過,我們直接上示例代碼:

// 1. 創(chuàng)建 Provider 和 Consumer
const {Provider, Consumer} = React.createContext('dark');
class Child extends React.Component {
  // 3. Consumer 組件接收一個函數(shù)作為子元素。這個函數(shù)接收當前的 context 值,并返回一個 React 節(jié)點。
  render() {
    return (
      <Consumer>
        {(theme) => (
        <button>
          {theme}
        </button>
      )}
      </Consumer>
    )
  }
}
class Parent extends React.Component {
  state = {
    theme: 'dark',
  };
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        theme: 'light'
      })
    }, 2000)
  }
  render() {
    // 2. 通過 Provider 的 value 傳遞值
    return (
      <Provider value={this.state.theme}>
        <Child />
      </Provider>
    )
  }
}

當 Provider 的 value 值發(fā)生變化時,它內(nèi)部的所有 consumer 組件都會重新渲染。

新 API 的好處就在于從 Provider 到其內(nèi)部 consumer 組件(包括 .contextType 和 useContext)的傳播不受制于 shouldComponentUpdate 函數(shù),因此當 consumer 組件在其祖先組件跳過更新的情況下也能更新。

模擬實現(xiàn)

那么 createContext 是怎么實現(xiàn)的呢?我們先不看源碼,根據(jù)前面的訂閱發(fā)布器的經(jīng)驗,我們自己其實就可以寫出一個 createContext 來,我們寫一個試試:

class Store {
    constructor() {
        this.subscriptions = []
    }
    publish(value) {
        this.subscriptions.forEach(f => f(value))
    }
    subscribe(f) {
        this.subscriptions.push(f)
    }
}
function createContext(defaultValue) {
    const store = new Store();
    // Provider
    class Provider extends React.PureComponent {
        componentDidUpdate() {
            store.publish(this.props.value);
        }
        componentDidMount() {
            store.publish(this.props.value);
        }
        render() {
            return this.props.children;
        }
    }
    // Consumer
    class Consumer extends React.PureComponent {
        constructor(props) {
            super(props);
            this.state = {
                value: defaultValue
            };
            store.subscribe(value => {
                this.setState({
                        value
                });
            });
        }
        render() {
            return this.props.children(this.state.value);
        }
    }
    return {
            Provider,
            Consumer
    };
}

用我們寫的 createContext 替換 React.createContext 方法,你會發(fā)現(xiàn),同樣可以運行。

它其實跟解決老 Context API 問題的方法是一樣的,只不過是做了一層封裝。Consumer 組件構(gòu)建的時候進行訂閱,當 Provider 有更新的時候進行發(fā)布,這樣就跳過了 PureComponent 的限制,實現(xiàn) Consumer 組件的更新。

createContext 源碼

現(xiàn)在我們?nèi)タ纯凑娴?createContext 源碼,源碼位置packages/react/src/ReactContext.js,簡化后的代碼如下:

import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
export function createContext(defaultValue) {
  const context = {
    $$typeof: REACT_CONTEXT_TYPE,
    // As a workaround to support multiple concurrent renderers, we categorize
    // some renderers as primary and others as secondary. We only expect
    // there to be two concurrent renderers at most: React Native (primary) and
    // Fabric (secondary); React DOM (primary) and React ART (secondary).
    // Secondary renderers store their context values on separate fields.
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    // Used to track how many concurrent renderers this context currently
    // supports within in a single renderer. Such as parallel server rendering.
    _threadCount: 0,
    // These are circular
    Provider: null,
    Consumer: null,
    // Add these to use same hidden class in VM as ServerContext
    _defaultValue: null,
    _globalName: null,
  };
  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  context.Consumer = context;
  return context;
}

你會發(fā)現(xiàn),如同之前的文章中涉及的源碼一樣,React 的 createContext 就只是返回了一個數(shù)據(jù)對象,但沒有關(guān)系,以后的文章中會慢慢解析實現(xiàn)過程。

React 系列

React 之 createElement 源碼解讀

React 之元素與組件的區(qū)別

React 之 Refs 的使用和 forwardRef 的源碼解讀

React 系列的預(yù)熱系列,帶大家從源碼的角度深入理解 React 的

以上就是React Context 變遷及背后實現(xiàn)原理詳解的詳細內(nèi)容,更多關(guān)于React Context 變遷原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React 無狀態(tài)組件(Stateless Component) 與高階組件

    React 無狀態(tài)組件(Stateless Component) 與高階組件

    這篇文章主要介紹了React 無狀態(tài)組件(Stateless Component) 與高階組件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • React 三大屬性之state的使用詳解

    React 三大屬性之state的使用詳解

    這篇文章主要介紹了React 三大屬性之state的使用詳解,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • React使用emotion寫css代碼

    React使用emotion寫css代碼

    這篇文章主要介紹了React如何使用emotion寫css代碼,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • 淺談使用React.setState需要注意的三點

    淺談使用React.setState需要注意的三點

    本篇文章主要介紹了淺談使用React.setState需要注意的三點,提出了三點對 React 新手來說是很容易忽略的地方,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • 使用React封裝一個Tree樹形組件的實例代碼

    使用React封裝一個Tree樹形組件的實例代碼

    這篇文章主要介紹了使用React封裝一個Tree樹形組件的實例,文中通過代碼示例講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-03-03
  • 基于react框架使用的一些細節(jié)要點的思考

    基于react框架使用的一些細節(jié)要點的思考

    下面小編就為大家?guī)硪黄趓eact框架使用的一些細節(jié)要點的思考。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • 詳解React中的useMemo和useCallback的區(qū)別

    詳解React中的useMemo和useCallback的區(qū)別

    React中的useMemo和useCallback是兩個重要的Hooks。常常被用于優(yōu)化組件的性能。雖然這兩個Hooks看起來很相似,但它們彼此之間還是有很大的區(qū)別的,隨著小編一起來學(xué)習(xí)吧
    2023-04-04
  • React中代碼分割的4種實現(xiàn)方式

    React中代碼分割的4種實現(xiàn)方式

    雖然一直有做react相關(guān)的優(yōu)化,按需加載、dll 分離、服務(wù)端渲染,但是從來沒有從路由代碼分割這一塊入手過,所以下面這篇文章主要給大家介紹了關(guān)于React中代碼分割的4種實現(xiàn)方式,需要的朋友可以參考下
    2022-01-01
  • react使用useState修改對象或者數(shù)組的值無法改變視圖的問題

    react使用useState修改對象或者數(shù)組的值無法改變視圖的問題

    這篇文章主要介紹了react使用useState修改對象或者數(shù)組的值無法改變視圖的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • React中閉包陷阱的幾種情及解決方案

    React中閉包陷阱的幾種情及解決方案

    在react中我們使用其提供的Hooks中的useState,useEffect,useCallback 時,可能會造成閉包陷阱,下面我們來看一下出現(xiàn)的情況以及如何解決,感興趣的小伙伴跟著小編一起來看看吧
    2024-07-07

最新評論

奉化市| 怀仁县| 都江堰市| 密山市| 江西省| 梁山县| 红河县| 弥渡县| 临朐县| 荥阳市| 宝应县| 孟津县| 汝城县| 巍山| 望城县| 集安市| 东方市| 绥棱县| 石家庄市| 安新县| 封丘县| 茶陵县| 重庆市| 疏附县| 九寨沟县| 钟山县| 宾川县| 清丰县| 南康市| 磐安县| 嘉义市| 鹿泉市| 石屏县| 黑河市| 西盟| 嵊州市| 大理市| 海安县| 盘山县| 光山县| 凭祥市|