你可能不知道的React用法?

WaterMan發表於2018-12-30

前言

React作為前端最?的框架之一,但是有的時候我們僅限於能用的階段,有一些高階用法,我們在日常開發中卻很少涉足。但是一旦用起來,我們就能發現它的方便和強大之處,我們就會越來越發現我們已經離不開它了!這就像是剛用React時,我內心是拒絕的,但是現在我已經離不開它了,越來越不能理解以前自己為什麼抱著JQuery不放呢!

今天我們重點講一下Context這個高階API,以及如何封裝它,讓它更加易用!

Context簡介

Context 通過元件樹提供了一個傳遞資料的方法,從而避免了在每一個層級手動的傳遞 props 屬性。

在一個典型的 React 應用中,資料是通過props屬性由上向下(由父及子)的進行傳遞的,但這對於某些型別的屬性而言是極其繁瑣的(例如:地區偏好,UI主題),這是應用程式中許多元件都所需要的。 Context 提供了一種在元件之間共享此類值的方式,而不必通過元件樹的每個層級顯式地傳遞 props 。

簡單說就是,當你不想在元件樹中通過逐層傳遞props或者state的方式來傳遞資料時,可以使用Context來實現跨層級的元件資料傳遞。

假設我們有一種場景,我們有一個業務容器App,裡面有一個元件容器Container,Container元件內包含一個Form表單,Form表單裡面有一個提交按鈕SubmitButton。假如使用props傳遞,我們就不得不傳遞四層。

你可能不知道的React用法?

你可能不知道的React用法?

看到了嗎?很方便吧!這裡我們使用Context,在元件可以直接通過context獲取最頂層繫結的值,避免了一層層傳遞props的麻煩,也減少出錯的可能性。

如何使用Context

如果要Context發揮作用,需要用到兩種元件,一個是Context生產者(Provider),通常是一個父節點,另外是一個Context的消費者(Consumer),通常是一個或者多個子節點。所以Context的使用基於生產者消費者模式。

Context 設計目的是為共享那些被認為對於一個元件樹而言是“全域性”的資料,例如當前認證的使用者、主題或首選語言。例如,在下面的程式碼中,我們通過一個“theme”屬性手動調整一個按鈕元件的樣式,使用context,我們可以避免通過中間元素傳遞props。

// 建立一個 theme Context,  預設 theme 的值為 light
const ThemeContext = React.createContext('light');

function ThemedButton(props) {
  // ThemedButton 元件從 context 接收 theme
  return (
    <ThemeContext.Consumer>
      {theme => <Button {...props} theme={theme} />}
    </ThemeContext.Consumer>
  );
}

// 中間元件
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}
複製程式碼

一種更簡單的使用方式

看了上面的使用方式,有沒有覺得還是有一些不爽,有沒有簡單一點的方式呢,或者能不能幫我封裝一下呢? 當然可以,Javascript工程師是無所不能的!!!

首先是我們的provider.js,這個就是我們封裝的context使用工具

import React, { Component } from 'react';

export const Context = React.createContext();

export class ContextProvider extends Component {
    render() {
        return (
            <Context.Provider value={this.props.context}>
                {this.props.children}
            </Context.Provider>
        );
    }
}

/**
 * 用註解的方式給子元件注入屬性
 */

export const injectContext = (contexts) => RealComponent => {
    return class extends Component {
        render() {
            return (
                <Context.Consumer>
                    {context => {
                        // 將頂層的context分發到各層
                        let mapContext = {};
                        if(Array.isArray(contexts)) {
                            contexts.map(item => {
                                mapContext[item] = context[item];
                            });
                        }
                        return (
                            <RealComponent {...mapContext} {...this.props} />
                        )
                    }}
                </Context.Consumer>
            );
        }
    };
};

複製程式碼

還是舉個例子,來讓大家明白上述封裝的方法的方便之處。 假如要實現GrandParent -> Parent -> Son,從GrandParent元件傳遞屬性到GrandSon元件,每個元件都有一個獨立的檔案。

先看入口檔案,我們在入口檔案進行繫結上下文,使用provider裡面的ContextProvider類,這裡我們主要繫結了propA和propB。

// 入口檔案

import React, { PureComponent } from 'react';
import { ContextProvider } from './provider';
import GrandParent from './GrandParent';

class Index extends PureComponent {
    render () {
        return (
            <ContextProvider context={{
                propA: 'propA',
                propB: 'propB'
            }}>
                <GrandParent />
            </ContextProvider>
        )
    }
}
複製程式碼

Parent元件沒什麼特殊的

import React, { PureComponent } from 'react';
import Son from './Son';

class Index extends PureComponent {
    render () {
        return (
            <Son />
        )
    }
}
複製程式碼

Son元件是真正使用屬性propA和propB的地方,我們通過ES6的Decorator實現,非常方便。注入後,可以像props一樣使用。

import React, { PureComponent } from 'react';
import { injectContext } from './provider';
import Son from './Son';

@injextContext(['propA', 'propB'])
class Index extends PureComponent {
    render () {
        return (
            <div>
                <span>propA為{this.props.propA}</span>
                <span>propB為{this.props.propB}</span>
            </div>
        )
    }
}
複製程式碼

後記

在這一小節中,我們主要講了React的一個高階語法Context,而且為了使用方便,我們封裝了ContextProvider類和injextContext方法,使用時利用ES6的Decorator語法糖,非常簡便。大家在日常開發中,也可以封裝出一些這樣的小工具,可以極大提升開發效率。

最後元旦快到了,祝大家新年快樂!!!

@Author: WaterMan

相關文章