国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

高階組件 + New Context API = ?

Joyven / 2826人閱讀

摘要:但是老版的存在一個嚴(yán)重的問題子孫組件可能不更新。其中,某一時刻屬性發(fā)生變化導(dǎo)致組件觸發(fā)了一次渲染,但是由于組件是且并未用到屬性,所以的變化不會觸發(fā)及其子孫組件的更新,導(dǎo)致組件未能得到及時的更新。

1. 前言

繼上次小試牛刀嘗到高價組件的甜頭之后,現(xiàn)已深陷其中無法自拔。。。那么這次又會帶來什么呢?今天,我們就來看看【高階組件】和【New Context API】能擦出什么火花!

2. New Context API

Context API其實(shí)早就存在,大名鼎鼎的redux狀態(tài)管理庫就用到了它。合理地利用Context API,我們可以從Prop Drilling的痛苦中解脫出來。但是老版的Context API存在一個嚴(yán)重的問題:子孫組件可能不更新。

舉個栗子:假設(shè)存在組件引用關(guān)系A(chǔ) -> B -> C,其中子孫組件C用到祖先組件A中Context的屬性a。其中,某一時刻屬性a發(fā)生變化導(dǎo)致組件A觸發(fā)了一次渲染,但是由于組件B是PureComponent且并未用到屬性a,所以a的變化不會觸發(fā)B及其子孫組件的更新,導(dǎo)致組件C未能得到及時的更新。

好在React@16.3.0中推出的New Context API已經(jīng)解決了這一問題,而且在使用上比原來的也更優(yōu)雅。因此,現(xiàn)在我們可以放心大膽地使用起來。說了那么多,都不如一個實(shí)際的例子來得實(shí)在。Show me the code:

// DemoContext.js
import React from "react";
export const demoContext = React.createContext();

// Demo.js
import React from "react";
import { ThemeApp } from "./ThemeApp";
import { CounterApp } from "./CounterApp";
import { demoContext } from "./DemoContext";

export class Demo extends React.PureComponent {
  state = { count: 1, theme: "red" };
  onChangeCount = newCount => this.setState({ count: newCount });
  onChangeTheme = newTheme => this.setState({ theme: newTheme });
  render() {
    console.log("render Demo");
    return (
      
        
        
      
    );
  }
}

// CounterApp.js
import React from "react";
import { demoContext } from "./DemoContext";

export class CounterApp extends React.PureComponent {
  render() {
    console.log("render CounterApp");
    return (
      

This is Counter application.

); } } class Counter extends React.PureComponent { render() { console.log("render Counter"); return ( {data => { const { count, onChangeCount } = data; console.log("render Counter consumer"); return (
{count}
); }}
); } } // ThemeApp.js import React from "react"; import { demoContext } from "./DemoContext"; export class ThemeApp extends React.PureComponent { render() { console.log("render ThemeApp"); return (

This is Theme application.

); } } class Theme extends React.PureComponent { render() { console.log("render Theme"); return ( {data => { const {theme, onChangeTheme} = data; console.log("render Theme consumer"); return (
); }} ); } }

雖說一上來就貼個百來行代碼的這種行為有點(diǎn)low,但是為了介紹New Context API的基本用法,也只能這樣了。。。不過啊,上面的例子其實(shí)很簡單,就算是先對New Context API的使用方法來個簡單的科普吧~

仔細(xì)觀察上面的代碼不難發(fā)現(xiàn)組件間的層級關(guān)系,即:Demo -> CounterApp -> Counter 和 Demo -> ThemeApp -> Theme,且中間組件CounterApp和CounterApp并沒有作為媒介來傳遞count和theme值。接下來,我們就來分析下上面的代碼,看看如何使用New Context API來實(shí)現(xiàn)祖先->子孫傳值的:

New Context API在React中提供了一個React.createContext方法,它返回的對象中包含了ProviderConsumer兩個方法。也就是DemoContext.js中的代碼。

顧名思義,Provider可以理解為公用值的一個提供者,而Consumer就是這個公用值的消費(fèi)者。那么兩者是如何聯(lián)系起來的呢?注意Provider接收的value參數(shù)。Provider會將這個value原封不動地傳給Consumer,這點(diǎn)也可以從Demo.js/CounterApp.js/ThemeApp.js三個文件中體現(xiàn)出來。

再仔細(xì)觀察例子中的value參數(shù),它是一個對象,key分別是count, theme, onChangeCount, onChangeTheme。很顯然,在Consumer中,我們不但可以使用count和theme,還可以使用onChangeCount和onChangeTheme來分別修改相應(yīng)的state,從而導(dǎo)致整個應(yīng)用狀態(tài)的更新和重新渲染。

下面我們再來看看實(shí)際運(yùn)行效果。從下圖中我們可以清楚地看到,CounterApp中的number和ThemeApp中的color都能正常地響應(yīng)我們的操作,說明New Context API確實(shí)達(dá)到了我們預(yù)期的效果。除此之外,不妨再仔細(xì)觀察console控制臺的輸出。當(dāng)我們更改數(shù)字或顏色時我們會發(fā)現(xiàn),由于CounterApp和ThemeApp是PureComponent,且都沒有使用count和theme,所以它們并不會觸發(fā)render,甚至Counter和Theme也沒有重新render。但是,這卻并不影響我們Consumer中的正常渲染。所以啊,上文提到Old Context API的子孫組件可能不更新的這個遺留問題算是真的解決了~~~

3. 說好的高階組件呢?

通過上面“生動形象”的例子,想必大家都已經(jīng)領(lǐng)會到New Context API的魔力,內(nèi)心是不是有點(diǎn)蠢蠢欲動?因?yàn)橛辛薔ew Context API,我們似乎不需要再借助redux也能創(chuàng)建一個store來管理狀態(tài)了(而且還是區(qū)域級,不一定非得在整個應(yīng)用的最頂層)。當(dāng)然了,這里并非是說redux無用,只是提供狀態(tài)管理的另一種思路。

咦~文章的標(biāo)題不是高階組件 + New Context API = ?嗎,怎么跑偏了?說好的高階組件呢?

別急,上面的只是開胃小菜,普及New Context API的基本使用方法而已。。。正菜這就來了~ 文章開頭就說最近沉迷高階組件無法自拔,所以在寫完上面的demo之后就想著能不能用高階組件再封裝一層,這樣使用起來可以更加順手。你別說,還真搞出了一套。。。我們先來分析上面demo中存在的問題:

我們在通過Provider傳給Consumer的value中寫了兩個函數(shù)onChangeCount和onChangeTheme。但是這里是不是有問題?假如這個組件足夠復(fù)雜,有20個狀態(tài)難道我們需要寫20個函數(shù)分別一一對應(yīng)更新相應(yīng)的狀態(tài)嗎?

注意使用到Consumer的地方,我們把所有的邏輯都寫在一個data => {...}函數(shù)中了。假如這里的組件很復(fù)雜怎么辦?當(dāng)然了,我們可以將{...}這段代碼提取出來作為Counter或Theme實(shí)例的一個方法或者再封裝一個組件,但是這樣的代碼寫多了之后,就會顯得重復(fù)。而且還有一個問題是,假如在Counter或Theme的其他實(shí)例方法中想獲取data中的屬性和update方法怎么辦?

為了解決以上提出的兩個問題,我要開始裝逼了。。。

3.1 Provider with HOC

首先,我們先來解決第一個問題。為此,我們先新建一個ContextHOC.js文件,代碼如下:

// ContextHOC.js
import React from "react";

export const Provider = ({Provider}, store = {}) => WrappedComponent => {
  return class extends React.PureComponent {
    state = store;
    updateContext = newState => this.setState(newState);
    render() {
      return (
        
          
        
      );
    }
  };
};

由于我們的高階組件需要包掉Provider層的邏輯,所以很顯然我們返回的組件是以Provider作為頂層的一個組件,傳進(jìn)來的WrappedComponent會被包裹在Provider中。除此之外還可以看到,Provider會接收兩個參數(shù)Provider和initialVlaue。其中,Provider就是用React.createContext創(chuàng)建的對象所提供的Provider方法,而store則會作為state的初始值。重點(diǎn)在于Provider的value屬性,除了state之外,我們還傳了updateContext方法。還記得問題一么?這里的updateContext正是解決這個問題的關(guān)鍵,因?yàn)镃onsumer可以通過它來更新任意的狀態(tài)而不必再寫一堆的onChangeXXX的方法了~

我們再來看看經(jīng)過Provider with HOC改造之后,調(diào)用方應(yīng)該如何使用。看代碼:

// DemoContext.js
import React from "react";
export const store = { count: 1, theme: "red" };
export const demoContext = React.createContext();

// Demo.js
import React from "react";

import { Provider } from "./ContextHOC";
import { ThemeApp } from "./ThemeApp";
import { CounterApp } from "./CounterApp";
import { store, demoContext } from "./DemoContext";

@Provider(demoContext, store)
class Demo extends React.PureComponent {
  render() {
    console.log("render Demo");
    return (
      
); } }

咦~ 原來與Provider相關(guān)的代碼在我們的Demo中全都不見了,只有一個@Provider裝飾器,想要公用的狀態(tài)全都寫在一個store中就可以了。相比原來的Demo,現(xiàn)在的Demo組件只要關(guān)注自身的邏輯即可,整個組件顯然看起來更加清爽了~

3.2 Consumer with HOC

接下來,我們再來解決第二個問題。在ContextHOC.js文件中,我們再導(dǎo)出一個Consumer函數(shù),代碼如下:

export const Consumer = ({Consumer}) => WrappedComponent => {
  return class extends React.PureComponent {
    render() {
      return (
        
          {data => }
        
      );
    }
  };
};

可以看到,上面的代碼其實(shí)非常簡單。。。僅僅是利用高階組件給WrappedComponent多傳了一個context屬性而已,而context的值則正是Provider傳過來的value。那么這樣寫有什么好處呢?我們來看一下調(diào)用的代碼就知道了~

// CounterApp.js
import React from "react";
import { Consumer } from "./ContextHOC";
import { demoContext } from "./DemoContext";

const MAP = { add: { delta: 1 }, minus: { delta: -1 } };

// ...省略CounterApp組件代碼,與前面相同

@Consumer(demoContext)
class Counter extends React.PureComponent {

  onClickBtn = (type) => {
    const { count, updateContext } = this.props.context;
    updateContext({ count: count + MAP[type].delta });
  };

  render() {
    console.log("render Counter");
    return (
      
{this.props.context.count}
); } } // ThemeApp.js import React from "react"; import { Consumer } from "./ContextHOC"; import { demoContext } from "./DemoContext"; // ...省略ThemeApp組件代碼,與前面相同 @Consumer(demoContext) class Theme extends React.PureComponent { onChangeTheme = evt => { const newTheme = evt.target.value; const { theme, updateContext } = this.props.context; if (newTheme !== theme) { updateContext({ theme: newTheme }); } }; render() { console.log("render Theme"); return (
) } }

可以看到,改造之后的Counter和Theme代碼一定程度上實(shí)現(xiàn)了去Consumer化。因?yàn)楹虲onsumer相關(guān)的邏輯僅剩一個@Consumer裝飾器了,而且我們只要提供和祖先組件中Provider配對的Consumer就可以了。相比最初的Counter和Theme組件,現(xiàn)在的組件也是更加清爽了,只需關(guān)注自身的邏輯即可。

不過需要特別注意的是,現(xiàn)在想要獲取Provider提供的公用狀態(tài)值時,改成了從this.props.context中獲取;想要更新狀態(tài)的時候,調(diào)用this.props.context.updateContext即可。

為什么?因?yàn)橥ㄟ^@Consumer裝飾的組件Counter和Theme現(xiàn)在就是ContextHOC文件中的那個WrappedComponent,我們已經(jīng)把Provider傳下來的Value作為context屬性傳給它了。所以,我們再次通過高階組件簡化了操作~

下面我們再來看看使用高階組件改造過后的代碼看看運(yùn)行的效果。

3.3 優(yōu)化

你以為文章到這里就要結(jié)束了嗎?當(dāng)然不是,寫論文的套路不都還要提出個優(yōu)化方法然后做實(shí)驗(yàn)比較么~ 更何況上面這張圖有問題。。。

沒錯,通過ContextHOC改造過后,上面的這張運(yùn)行效果圖似乎看上去沒有問題,但是仔細(xì)看Console控制臺的輸出你就會發(fā)現(xiàn),當(dāng)更新count或theme任意其中一個的時候,Counter和Theme都重新渲染了一次!!!可是,我的Counter和Theme組件明明都已經(jīng)是PureComponent了啊~ 為什么沒有用!!!

原因很簡單,因?yàn)槲覀儌鹘oWrappedComponent的context每次都是一個新對象,所以就算你的WrappedComponent是PureComponent也無濟(jì)于事。。。那么怎么辦呢?其實(shí),上文中的Consumer with HOC操作非常粗糙,我們直接把Provider提供的value值直接一股腦兒地傳給了WrappedComponent,而不管WrappedComponent是否真的需要。因此,只要我們對傳給WrappedComponent的屬性值精細(xì)化控制,不傳不相關(guān)的屬性就可以了。來看看改造后的Consumer代碼:

// ContextHOC.js
export const Consumer = ({Consumer}, relatedKeys = []) => WrappedComponent => {
  return class extends React.PureComponent {
    _version = 0;
    _context = {};
    getContext = data => {
      if (relatedKeys.length === 0) return data;
      [...relatedKeys, "updateContext"].forEach(k => {
        if(this._context[k] !== data[k]) {
          this._version++;
          this._context[k] = data[k];
        }
      });
      return this._context;
    };
    render() {
      return (
        
          {data => {
            const newContext = this.getContext(data);
            const newProps = { context: newContext, _version: this._version, ...this.props };
            return ;
          }}
        
      );
    }
  };
};

// 別忘了給Consumer組件指定relatedKeys

// CounterApp.js
@Consumer(demoContext, ["count"])
class Counter extends React.PureComponent {
  // ...省略
}

// ThemeApp.js
@Consumer(demoContext, ["theme"])
class Theme extends React.PureComponent {
  // ...省略
}

相比于第一版的Consumer函數(shù),現(xiàn)在這個似乎復(fù)雜了一點(diǎn)點(diǎn)。但是其實(shí)還是很簡單,核心思想剛才上面已經(jīng)說了,這次我們會根據(jù)relatedKeys從Provider傳下來的value中匹配出WrappedComponent真正想要的屬性。而且,為了保證傳給WrappedComponent的context值不再每次都是一個新對象,我們將它保存在了組件的實(shí)例上。另外,只要Provider中某個落在relatedKeys中的屬性值發(fā)生變化,this._version值就會發(fā)生變化,從而也保證了WrappedComponent能夠正常更新。

最后,我們再來看下經(jīng)過優(yōu)化后的運(yùn)行效果。

4. 寫在最后

經(jīng)過今天這波操作,無論是對New Context API還是HOC都有了更深一步的理解和運(yùn)用,所以收貨還是挺大的。最重要的是,在現(xiàn)有項(xiàng)目不想引進(jìn)redux和mobx的前提下,本文提出的這種方案似乎也能在一定程度上解決某些復(fù)雜組件的狀態(tài)管理問題。

當(dāng)然了,文中的代碼還有很多不嚴(yán)謹(jǐn)?shù)牡胤剑€需要繼續(xù)進(jìn)一步地提升。完整代碼在這兒,歡迎指出不對或者需要改進(jìn)的地方。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/98376.html

相關(guān)文章

  • react new context API的一次實(shí)踐(補(bǔ)充)

    摘要:作為數(shù)據(jù)的發(fā)布方,它擁有一個名為的屬性,用于維護(hù)數(shù)據(jù)內(nèi)容,通過傳遞給的數(shù)據(jù)會被發(fā)布出去。最后,組件將自己的原封不動的傳遞給。但是通過這次的實(shí)踐,也算是熟悉的的用法,對也加深了了解吧。 這是一篇我發(fā)在掘金上的文章,原文有一個我沒有解決的問題,在網(wǎng)友的解答下我找到了答案,我把文章重新修改編輯后,同步發(fā)送到這里,希望能對大家有所幫助。本文原發(fā)布于掘金:https://juejin.im/po...

    kaka 評論0 收藏0
  • 【React進(jìn)階系列】手寫實(shí)現(xiàn)react-redux api

    簡介:簡單實(shí)現(xiàn)react-redux基礎(chǔ)api react-redux api回顧 把store放在context里,所有子組件可以直接拿到store數(shù)據(jù) 使組件層級中的 connect() 方法都能夠獲得 Redux store 根組件應(yīng)該嵌套在 中 ReactDOM.render( , rootEl ) ReactDOM.render( ...

    劉玉平 評論0 收藏0
  • React組件設(shè)計(jì)技巧

    摘要:我們可以通過剛剛高階函數(shù)的思想來創(chuàng)建一個中間組件,也就是我們說的高階組件。僅傳遞組件所需要的屬性。在受控組件中,表單數(shù)據(jù)由組件負(fù)責(zé)處理。作為頂層組件接收一個名為的,可以接收任意需要被放入中的字符串,數(shù)字,甚至是函數(shù)。 React組件設(shè)計(jì) 組件分類 展示組件和容器組件 展示組件 容器組件 關(guān)注事物的展示 關(guān)注事物如何工作 可能包含展示和容器組件,并且一般會有DOM標(biāo)簽和cs...

    Luosunce 評論0 收藏0
  • React的組件模式

    摘要:有狀態(tài)組件通常使用和生命周期相關(guān)事件。組件模式是使用時的最佳實(shí)踐,最初引入組件模式是為了將數(shù)據(jù)邏輯和表現(xiàn)層進(jìn)行分離。這是一種可以對輸入組件的進(jìn)行修改增刪改查然后返回全新的修改后的組件強(qiáng)大模式,想想和。 showImg(https://segmentfault.com/img/bVborAV?w=2560&h=1440); 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著...

    Muninn 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<