Why Context
在写 React 网站的时候,因为资料都是由上而下的藉由元件传递,有些情况,例如:整体 UI 主题、使用者身份验证、偏好语言...等广泛应用的资料,就会显得太笨重。同样的资料要同时传递给很多下层元件、而且这些元件可能都在深处,会让 code 更加凌乱且难以阅读。对此,React 提供了一种方法:Context,让资料的分享可以不需要如此繁复。
How To Use Context
Context 的设计理念是用来分享那些在元件树中,称得上 global 的资讯。例如下面的情境,就是当使用者,点选了「黑夜版」的网页:
// 使用 createContext 方法,为目前的网站色调建立 context,并给予一个预设值
const ThemeContext = React.createContext('morning');
class App extends React.Component {
render() {
// 使用 Provider 方法,把上面建立的 context 传递给底下的元件,这里重新赋予新的黑夜版样式
// 不论元件埋的多深,都可以读到
return (
<ThemeContext.Provider value="night">
<Menu />
....里面的 component 都可以收到
</ThemeContext.Provider>
);
}
}
// Menu 作为传递的中间人,不需要有把 props 往下传的行为
function Menu() {
return (
<div>
<MenuButton />
<MenuButton />
<MenuButton />
</div>
);
}
class MenuButton extends React.Component {
// 要阅读到我们一开始定义的 ThemeContext,需要把它赋值给 contextTyp
// 这里的运作是这样子:React 会去找到最接近且吻合的 theme provider 并使用它的值(也就是'night')
static contextType = ThemeContext;
render() {
return <Button color={this.context} />;
}
}
你要传递下去的资讯,也有可能是一大包,收拢在另外的文件里,如以下的案例,此时,可以这样传递:
///// theme 的定义区块 theme.js
export const themes = {
morning: {
fontColor: 'black',
background: 'light-grey',
q
},
night: {
fontColor: 'white',
background: 'blue',
},
};
export const ThemeContext = React.createContext(
themes.night
);
///// theme 的使用者 footer.js
import {ThemeContext} from './theme-context';
class ThemeFooter extends React.Component {
render() {
let props = this.props;
let theme = this.context;
return (
<Footer
{...props}
style={{backgroundColor: theme.background}}
/>
);
}
}
// 这里把 import 进来的样式表,塞进元件的 contextType 里
ThemeFooter.contextType = ThemeContext;
export default ThemeFooter;
///// theme 的切换区块 app.js
import {ThemeContext, themes} from './theme';
import ThemeFooter from './footer';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
theme: themes.morning,
};
}
render() {
return (
<Page>
<ThemeContext.Provider value={this.state.theme}>
</ThemeFooter>
//....其他需要使用样式的元件
</ThemeContext.Provider>
</Page>
);
}
}
ReactDOM.render(<App />, document.root);
什麽情况下不该用 Context ?
过度的使用 context,会让你的元件变得不好复用,如果你单纯只是讨厌把资料传递很多层(而非那些广泛运用的情境),可以考虑别的作法。以下举一个例子:假设网站的 照片、使用者名称、性别... 在最上层取得,需要经过页面 layout、Header、Menu 区域...才能抵达最後会用到资讯的 使用者头像 元件,除了层层传递或者用 context(不建议),还可以如何解决?
解决方法之一是,不要单独传递 props 资讯下去,你可以直接传递一整个使用者头像元件,中间用来传递的元件,就不需要看到那些细碎的使用者资料。
// 这里是最上层取得使用者资讯的地方
function Page(props){
// 把使用者头像在上层,连同要用到的资讯打包好
const userAvatar = <Avatar user={user} size={props.avatarSize} />;
// 再把 使用者头像元件 当成 props 传递下去
return <PageLayout userAvatar={userAvatar} />;
}
然而,这样的做法也并非适用於每个案例,当我们把 userAvatar 在上层打包好,也意味着会增加上层元件的复杂程度。
这样子的 props 并不局限於只能应用一组资料,你也可以为要传递下去的 props 建立很多个不同的「插槽」(如范例)。如果你传递下去的这些元件,有与上层元件沟通的需求,你可以考虑 Render Props 的方法。
function Page(props){
const blockContent = <Description user={user} />;
const header = <Avatar user={user} size={props.avatarSize} />;
const detail = <Detail infor={props.infor} />;
return (
<PageLayout
header={header}
content={blockContent}
moreInfor={detail}
/>
);
}
以上的案例情境都较为单纯,但实际在使用 context 的时候,情况可能更加复杂,有时候会有多组的 context 需要传递,或者 theme toggle 也需要一并传递到子元件的情况...,请见下篇文章。
<<: Material UI in React [Day 23] Data Display (part 3) 表格 & 提示
>>: [Day 12] 决策树 (Decision tree)
去年因为肺炎导致广告收入骤降,加上iOS的IDFA政策的双重打击,我们决定开始做月费制的功能。 上线...
我们之前做的单元测试, 比较接近针对API的测试, 那我们现在要开始针对网页来做测试, 我们首先针对...
杰哥的考研纪录 tags: IT铁人 首先先跟各位打个招呼! 欢迎来到杰哥的考研小天地~ 这篇会简单...
前言 我是先把 Kernel 等相关套件,以及 VirtualBox 安装起来之後,再从错误讯息去尝...
朋友送了一组键盘滑鼠.Logitech 键盘yr0009 & 滑鼠M215 想要滑鼠放家里用...