1. 程式人生 > >React Context和高階元件HOC用法

React Context和高階元件HOC用法

前言


本文將通過官方例項,組合運用Context高階元件,並給出例項程式碼的方式詳細介紹Context、高階元件的概念和用法。

Context


在典型的React應用中,資料是通過props屬性一層層的由上向下傳遞。但是對於應用中很多元件都需要使用的值(比如說:頁面的主題,頁面語言選擇),如果還是通過props屬性一層層的傳遞下來,會非常繁瑣。

Context通過元件樹提供了一種傳遞資料的方法,可以避免在每一層手動的傳遞props屬性,卻能讓元件共享此類資料。

設計目的

Context的設計目的是為了共享那些被元件樹認為是“全域性”的資料。

注意: 
不要僅僅為了避免使用props

在幾個層級下的元件傳遞資料,而使用Context,它被用於多個層級的多個元件需要使用相同資料的場景。

高階元件


高階元件是一個沒有副作用的純函式,該函式接受一個元件作為引數,並返回一個新的元件。高階元件並不是一個React API,而是一種模式。

元件是將props屬性轉成UI,而高階元件是將元件轉成一個新的元件。高階元件通過將原元件包裹在容器元件裡面的方式來組合使用原元件。

如果大家使用Redux作為狀態管理容器,那麼大家對React-Redux中的connect很熟悉,面試的時候,我老是把connect說成高階元件,但是它的形式卻並不附和我們上面介紹的高階元件。其實connect

是一個返回接受單引數的高階元件的高階函式。connect返回的高階元件的形式是下面這樣的:

component => component
  •  

上述形式的高階元件有個顯著優點:輸入和輸出型別相同的函式是很容易組合在一起的。

下面我們將通過例子詳細介紹高階元件到底可以做什麼。

例項

我們以頁面主題為例,使用Context實現資料的傳遞。並實現動態Context的功能,通過新增一個功能按鈕,實現Context傳遞的資料的切換。

第一步:我們首先建立一個Context.js

//Context.js
export const themes = {
    light: {
        foreground: '#ffffff',
        background: '#222222',
    },
    dark: {
        foreground: '#000000',
        background: '#eeeeee',
    },
};

export const ThemeContext = React.createContext(
    themes.dark // default value
);

上述程式碼使用了React.createContext,用法如下:

const {Provider, Consumer} = React.createContext(defaultValue);

當 React 渲染 context 元件 Consumer 時,它將從元件樹的上層中最接近的匹配的 Provider 讀取當前的 context 值。 
如果上層沒有匹配的Provider,而此時我們渲染了一個Consumer元件,那麼此時就會用到我們建立Context的時候用到的defaultValue

第二步,我們原本會打算建立需要使用頁面主題資料的元件,但是我們想一下:我們的React應用不可能只有一個元件會用到我們定義的主題Context,如果給每個元件都一個個的手動引用主題Context,會很麻煩。這個時候我們就可以使用高階元件了。所以我們建立下面這個高階元件:

//WithTheme.js
import { ThemeContext } from "./context"
export function WithTheme(Component) {
     return class extends Component{
        render(){
            const { props } = this;
            return(
                <ThemeContext.Consumer>
                    {theme => <Component {...props} theme = {theme}/>}
                </ThemeContext.Consumer>
            )
        }
    }
}

從上面可以看出來,想要使用定義的Context,只需要在原元件包裹在React.createContext生成的Consumer裡面就可以了。

接下來,我們建立一個需要使用頁面主題資料的元件ThemeButton.js

//ThemeButton.js

class ThemeButton extends Component{
    constructor(props){
        super(props);
        this.title = "button component"
    }
    render(){
        const {props} = this;

        return(
            <div>
                <button style={{color:props.theme.foreground,background:props.theme.background}}>{props.title}</button>
            </div>
        )
    }
}
//WithTheme就是上面定義的高階元件
export default WithTheme(ThemeButton);

接下來我們建立一個展示元件,裡面包含上述ThemeButton元件和一個用於切換主題的按鈕。程式碼如下:

//Toolbar.js
import ThemeButton from "./ThemeButton"
class Toolbar extends Component{
    constructor(props){
        super(props);
    }
    render(){
        const { props } = this;
        return(
            <div>
                <ThemeButton title="按我吧" />
                //這裡的changeTheme處理函式從父元件傳入
                <button  onClick = { props.changeTheme }>
                    別按我
                </button>
            </div>


        )
    }
}
export default Toolbar;

最後我們建立一個容器元件,用於管理所有的資料和處理程式:

//Container.js
import { ThemeContext,themes } from "./context"
import Toolbar from "./Toolbar"
class Container extends Component{
    constructor(props){
        super(props);
        this.state ={
            theme:themes.light
        };
        this.toggleTheme = this.toggleTheme.bind(this);
    }
    toggleTheme(){
        this.setState(state =>({
            theme:state.theme === themes.dark ? themes.light:themes.dark
        }))
    }
    render(){
        return(
            <div>
                <ThemeContext.Provider value={this.state.theme}>
                    <Toolbar changeTheme={this.toggleTheme} />
                </ThemeContext.Provider>
            </div>
        )
    }
}

export default  Container;
  • 可以看出來,想要為使用ThemeContext的元件提供值,只需要使用React.createContext生成的Provider包裹展示元件就可以了。