组件化优点
- 增强代码重用性,提高开发效率
- 简化调试步骤,提升整个项目的可维护性
- 便于协同开发
- 注意点:降低耦合性
组件跨层级通信-Context
React应用中,大部分数据是通过props属性自上而下(由父及子)进行传递的,遇到复杂的嵌套组件关系时,可以使用Context实现祖代组件想后代组件跨层级传值,类似于Vue中的provide&inject就是来源于Context
Context API
React.createContext
:创建一个Context对象,当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中里自身最近的那个匹配的Provider中读取到当前的context值
Context.Provider
:Provider接收一个value属性,传递给消费组件,允许消费组件订阅context的变化。一个Provider可以和多个消费组件有对应关系。多个Provider也可以嵌套使用,里层的会覆盖外层的数据
当Provider的value值发生变化时,它内部的所有消费组件都会重新渲染。Provider及其内部consumer组件不受制于shouldComponentUpdate函数,因此当consumer组件在其祖先组件退出更新的情况下也能更新
Class.contextType
:挂载在class上的contextType属性会被重新赋值为一个由React.createContext()创建的Context对象。这能让你使用this.context来消费最近的Context上的那个值。你可以在任何生命周期中访问到它,包括render函数中
你只能通过该API订阅单一context
Context.Consumer
:这里,React组件也可以订阅到context变更。这能让你在函数组件中完成订阅context。这个函数接收当前的context值,返回一个React节点。传递给函数的value值等同于往上组件树离这个context最近的Provider提供的value值。如果没有对应的Provider,value参数等同于传递给createContext()的defaultValue
useContext
:接收一个context对象(React.createContext的返回值)并返回该context的当前值。当前的context值由上层组件中距离当前组件最近的<MyContext.Provider>
的value
prop决定。只能用在function组件中
如何使用Context:三步走
step1:创建一个context对象
step2:创建Provider,传递value
step3:子组件消费value,有三种途径:contextType、useContext、Consumer
区别
contextType只能用在类组件,只能订阅单一的context来源
useContext只能用在函数组件或自定义hook中,可以读取多个
Consumer不限制函数或者类组件
例子:使用Context共享主题色
创建Context => 获取Provider和Consumer =>Provider提供值 =>Consumer消费值
Context.js
import React from "react";
// 创建Context对象,React.createContext
export const ThemeContext = React.createContext({ themeColor: "pink" });
export const UserContext = React.createContext();
ContextPage.js
import React, { Component } from "react";
import ContextTypePage from "./ContextTypePage";
import { ThemeContext, UserContext } from "./Context";
import ConsumerPage from "./ConsumerPage";
import UseContextPage from "./UseContextPage";
class ContextPage extends Component {
constructor(props) {
super(props);
this.state = {
theme: {
themeColor: "red"
},
user: {
name: "xiaoming"
}
};
}
changeColor = () => {
const { themeColor } = this.state.theme;
this.setState({
theme: {
themeColor: themeColor === "red" ? "green" : "red"
}
});
};
render() {
const { theme, user } = this.state;
return (
<div>
<h3>ContextPage</h3>
<button onClick={this.changeColor}>change color</button>
{/* Provide一个context */}
<ThemeContext.Provider value={theme}>
<ContextTypePage />
<UserContext.Provider value={user}>
{/* 函数组件 */}
<UseContextPage />
<ConsumerPage />
</UserContext.Provider>
</ThemeContext.Provider>
</div>
);
}
}
export default ContextPage;
ContextTypePage.js
类组件使用class.contextType方式
import React, { Component } from "react";
import { ThemeContext } from "./Context";
// class组件
class ContextTypePage extends Component {
static contextType = ThemeContext; // Class.contextType使其能用this.context
render() {
const { themeColor } = this.context;
return (
<div>
<h3 className={themeColor}>ContextTypePage</h3>
</div>
);
}
}
export default ContextTypePage;
UseContextPage.js
函数组件使用useContext()hook方式
import React, { useContext } from "react";
import { ThemeContext, UserContext } from "./Context";
function UseContextPage(props) {
// useContext可以使用订阅多个context
const themeContext = useContext(ThemeContext);
const { themeColor } = themeContext;
const userContext = useContext(UserContext);
return (
<div>
<h3 className={themeColor}>UseContextPage</h3>
<p>{userContext.name}</p>
</div>
);
}
export default UseContextPage;
ConsumerPage.js
不限制函数和类组件
import React, { Component } from "react";
import { ThemeContext, UserContext } from "./Context";
class ConsumerPage extends Component {
render() {
return (
<div>
{/* 除了使用Class.contextType、useContext之外还有Consumer */}
<ThemeContext.Consumer>
{themeContext => (
<>
<h3 className={themeContext.themeColor}>ConsumerPage</h3>
<UserContext.Consumer>
{userContext => <HandleUserPage {...userContext} />}
</UserContext.Consumer>
</>
)}
</ThemeContext.Consumer>
</div>
);
}
}
function HandleUserPage(userCtx) {
return <div>{userCtx.name}</div>;
}
export default ConsumerPage;