教你如何優雅的使用React的context屬性
專案程式碼和最新內容,請在我的github閱讀,請注意該資料夾下4個可以執行的例子。歡迎您star, issue
首先:我們來介紹一個高階函式,該函式對我們的原始元件進行裝飾,並提供了訪問應用context中的相應宣告:
Inject.contextTypes = {
data: React.PropTypes.object,
get: React.PropTypes.func,
register: React.PropTypes.func
};
其實這相當於指明瞭應用的store簽名,之所以以HOC的方式來指明是因為這樣的話,每一個元件都可以通過該方法來訪問context中的內容,而不用在每一個元件中都宣告contextTypes。下面是函式的簽名:
//wire.js
import React from 'react';
/**
* [wire description]
* @param {[type]} Component 我們的元件Component的class
* @param {[type]} dependencies 在context中那些屬性是要被作為props傳遞到我們的最終的元件中的
* @param {[type]} mapper 對我們要傳入到元件的屬性進行進一步的處理
* @return {[type]} [description]
* 該方法的呼叫方式如下:
* wire(Title, ['config'], function (config) {
return { title: config.name };
});
*/
export default function wire(Component, dependencies, mapper) {
class Inject extends React.Component {
render() {
var resolved = dependencies.map(this.context.get.bind(this.context));
//這是我們的context中的自己關心的那一部分內容
var props = mapper(...resolved);
//將我們自己關心的那一部分資料放入到我們的mapper函式中繼續處理成為我們Component最終的props
return React.createElement(Component, props);
}
}
//這是我們的dependencies的內部簽名,但是是通過高階元件的方式來定義的,所以通過wire方法處理的元件都是可以獲取
//到我們最外層的App元件中在context中宣告的物件的
Inject.contextTypes = {
data: React.PropTypes.object,
get: React.PropTypes.func,
register: React.PropTypes.func
};
return Inject;
};
到了這裡我們先來看一下我們的context中的資料結構:
//dependencies.js
export default {
data: {},
get(key) {
return this.data[key];
},
register(key, value) {
this.data[key] = value;
}
};
也就是說我們所有的context中的資料是一個物件,但是該物件有get方法和register方法來向context中註冊資料。
接下來我們看看元件中如何使用上面這個函式:
export default {
name: 'React in patterns'
};
上面是我們應用的配置資訊。
//App.js
import React from 'react';
import ReactDOM from 'react-dom';
import Header from './Header.jsx';
//Header物件
import CONFIG from './config';
import dependencies from './dependencies';
//這樣我們的context中已經註冊了config物件了
dependencies.register('config', CONFIG);
//CONFIG物件是如下簽名:
//{
// name: 'React in patterns'
// };
class App extends React.Component {
getChildContext() {
return dependencies;
}
render() {
return <Header />;
}
};
//這是我們放入到context中的dependencies物件
App.childContextTypes = {
data: React.PropTypes.object,
get: React.PropTypes.func,
register: React.PropTypes.func
};
ReactDOM.render(<App />, document.querySelector('#container'));
也就是說,我們在最上層的元件的context中註冊了我們應用所需要的配置資訊,然後逐層向下傳遞。我們看看Header.js的內容
import React from 'react';
import Title from './Title.jsx';
//Header元件裡面繼續使用Title元件
export default function Header() {
return (
<header>
<Title />
</header>
);
}
我們繼續來看看Title.js的內容:
import React from 'react';
import wire from './wire';
function Title(props) {
return <h1>{ props.title }</h1>;
}
//Title元件必須指定Title.proTypes
Title.propTypes = {
title: React.PropTypes.string
};
//這是我們最深層次的元件例項
export default wire(Title, ['config'], function (config) {
return { title: config.name };
});
到了這裡,你應該更多的關注於wire方法。在wire方法裡面,我們通過HOC的方式來實現了contextTypes。因此,經過包裝的Title元件是可以獲取到context中的所有資訊的。其首先獲取到config屬性,該屬性在App元件中通過register方法進行了註冊,獲取到config以後我們會將config物件傳入到mapper中,從而返回了我們Component需要的props屬性,即 {title: config.name}。這樣的話,我們的Componet就可以根據這些props屬性進行UI渲染了。
這樣做的好處:
(1)高階元件中聲明瞭contextTypes,只要經過wire方法包裝的元件都是可以獲取到context中的值的。這樣可以避免contextTypes需要在每一個元件中都宣告一次
(2)使用了context,當元件樹巢狀過深的情況下可以防止中間層元件需要處理那些它不需要關心的屬性。