React组件之间的通信

React 的组件相对于 Vue 更加的灵活和多样,按照不同的方式可以分成很多类组件:
根据组件的定义方式
,可以分为:函数组件(Functional Component )
和类组件(Class Component)
;根据组件内部是否有状态需要维护
,可以分成:无状态组件(Stateless Component )
和有状态组件(Stateful Component)
;根据组件的不同职责
,可以分成:展示型组件(Presentational Component)
和容器型组件(Container Component)
;
React 中的组件之间的通信方法。
父子组件通信
下面这个案例演示了
子传父
与父传子
通过 props 传递。
父组件
import React, { Component } from "react";
import Header from "./Header";
import Footer from "./Footer";
import Main from "./Main";
class App extends Component {
constructor() {
super();
this.state = {
title: "Header的标题",
};
}
headerClick() {
console.log("----");
}
render() {
const { title } = this.state;
return (
<div>
<Header title={title} headerClick={() => this.headerClick()} />
<Main />
<Footer />
</div>
);
}
}
export default App;
子组件
import React, { Component } from "react";
export class Header extends Component {
// constructor(props) {
// super(props)
// this.state = {}
// }
render() {
const { title, headerClick } = this.props;
return <div onClick={headerClick}>Header: {title}</div>;
}
}
export default Header;
非父子组件通信
非父子组件数据的共享
在开发中,比较常见的数据传递方式是通过 props 属性自上而下(由父到子)进行传递。
但是对于有一些场景:比如一些数据需要在多个组件中进行共享(地区偏好、UI 主题、用户登录状态、用户信息等)。
如果我们在顶层的 App 中定义这些信息,之后一层层传递下去,那么对于一些中间层不需要数据的组件来说,是一种冗余的操作。
React 提供了一个 API:Context
;
Context 提供了一种在组件之间共享此类值的方式
,而不必显式地通过组件树的逐层传递 props
;
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据
,例如当前认证的用户、主题或首选语言;
可以使用Context
或者eventBus
React.createContext
创建一个需要共享的 Context 对象:
如果一个组件订阅了 Context,那么这个组件会从离自身最近的那个匹配的 Provider 中读取到当前的 context 值;
defaultValue 是组件在顶层查找过程中没有找到对应的 Provider,那么就使用默认值
const MyContext = React.createContext(defaultValue);
Context.Provider
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化:
Provider 接收一个 value 属性,传递给消费组件;
一个 Provider 可以和多个消费组件有对应关系;
多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据;
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染;
<MyContext.Provider value={/* 某个值 */} />
案例
以下案例同时演示Context
与eventBus
context.jsx
import React from "react";
export const UserContext = React.createContext({ name: "kobe", age: 30 });
app.jsx
import React, { Component } from "react";
import Home from "./Home";
import Recommend from "./Recommend";
import { UserContext } from "./context";
import eventBus from "./event/event_bus";
class App extends Component {
constructor() {
super();
this.state = {
name: "anzhiyu",
};
}
componentDidMount() {
eventBus.on("changeName", this.productClick, this);
}
componentWillUnmount() {
eventBus.off("changeName", this.productClick);
}
productClick(name, age) {
console.log("productClick", name, age, this);
this.setState({ name: name });
}
render() {
return (
<div className="app">
<UserContext.Provider value={{ name: "安知鱼", level: 100 }}>
<Home />
<Recommend />
</UserContext.Provider>
<h2>{this.state.name}</h2>
</div>
);
}
}
export default App;
Home.jsx
import React, { Component } from "react";
import HomeProduct from "./HomeProduct";
class Home extends Component {
render() {
return (
<div>
<HomeProduct />
</div>
);
}
}
export default Home;
HomeProduct.jsx
import React, { Component } from "react";
import { UserContext } from "./context";
import eventBus from "./event/event_bus";
class HomeProduct extends Component {
productClick() {
eventBus.emit("changeName", "安知鱼变帅", 18);
}
render() {
console.log(this.context);
return (
<div>
<div>HomeProduct</div>
<button onClick={e => this.productClick()}>product</button>
</div>
);
}
}
HomeProduct.contextType = UserContext;
export default HomeProduct;
Recommend.jsx 演示 函数式组件 Consumer
import { UserContext } from "./context";
function Recommend(props) {
return (
<div>
<span>Recommend</span>
{/* 函数式组件中使用Context共享的数据 */}
<UserContext.Consumer>
{value => {
return <span>{value.name}</span>;
}}
</UserContext.Consumer>
</div>
);
}
export default Recommend;
如果需要使用多个 context 可以使用Consumer
共享
theme-context.js
import React from "react";
// 1.创建一个Context
const ThemeContext = React.createContext({ color: "blue", size: 10 });
export default ThemeContext;
user-context.js
import React from "react";
// 1.创建一个Context
const UserContext = React.createContext();
export default UserContext;
HomeInfo.jsx
import React, { Component } from "react";
import ThemeContext from "./context/theme-context";
import UserContext from "./context/user-context";
export class HomeInfo extends Component {
render() {
// 4.第四步操作: 获取数据, 并且使用数据
console.log(this.context);
return (
<div>
<h2>HomeInfo: {this.context.color}</h2>
<UserContext.Consumer>
{value => {
return <h2>Info User: {value.nickname}</h2>;
}}
</UserContext.Consumer>
</div>
);
}
}
// 3.第三步操作: 设置组件的contextType为某一个Context
HomeInfo.contextType = ThemeContext;
export default HomeInfo;
context 默认数据
当没有被 context 组件包裹时就会使用到默认数据
theme-context.js
import React from "react";
// 1.创建一个Context
const ThemeContext = React.createContext({ color: "blue", size: 10 });
export default ThemeContext;
App.jsx
import React, { Component } from "react";
import Home from "./Home";
import ThemeContext from "./context/theme-context";
import UserContext from "./context/user-context";
import Profile from "./Profile";
export class App extends Component {
constructor() {
super();
this.state = {
info: { name: "kobe", age: 30 },
};
}
render() {
const { info } = this.state;
return (
<div>
<h2>App</h2>
<UserContext.Provider value={{ nickname: "kobe", age: 30 }}>
<ThemeContext.Provider value={{ color: "red", size: "30" }}>
<Home {...info} />
</ThemeContext.Provider>
</UserContext.Provider>
<Profile />
</div>
);
}
}
export default App;
Profile.jsx
import React, { Component } from "react";
import ThemeContext from "./context/theme-context";
export class Profile extends Component {
render() {
console.log(this.context);
return <div>Profile</div>;
}
}
Profile.contextType = ThemeContext;
export default Profile;