React 的组件相对于 Vue 更加的灵活和多样,按照不同的方式可以分成很多类组件:
根据组件的定义方式
,可以分为:函数组件(Functional Component )
和类组件(Class Component)
;根据组件内部是否有状态需要维护
,可以分成:无状态组件(Stateless Component )
和有状态组件(Stateful Component)
;根据组件的不同职责
,可以分成:展示型组件(Presentational Component)
和容器型组件(Container Component)
;
React 中的组件之间的通信方法。
父子组件通信
下面这个案例演示了子传父
与父传子
通过 props 传递。
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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;
|
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, { Component } from "react";
export class Header extends Component {
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,那么就使用默认值
1
| const MyContext = React.createContext(defaultValue);
|
Context.Provider
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化:
Provider 接收一个 value 属性,传递给消费组件;
一个 Provider 可以和多个消费组件有对应关系;
多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据;
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染;
1
| <MyContext.Provider value={} />
|
案例
以下案例同时演示Context
与eventBus
context.jsx
1 2 3
| import React from "react";
export const UserContext = React.createContext({ name: "kobe", age: 30 });
|
app.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React, { Component } from "react"; import HomeProduct from "./HomeProduct";
class Home extends Component { render() { return ( <div> <HomeProduct /> </div> ); } }
export default Home;
|
HomeProduct.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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
1 2 3 4 5 6
| import React from "react";
const ThemeContext = React.createContext({ color: "blue", size: 10 });
export default ThemeContext;
|
user-context.js
1 2 3 4 5 6
| import React from "react";
const UserContext = React.createContext();
export default UserContext;
|
HomeInfo.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import React, { Component } from "react"; import ThemeContext from "./context/theme-context"; import UserContext from "./context/user-context";
export class HomeInfo extends Component { render() { 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> ); } }
HomeInfo.contextType = ThemeContext;
export default HomeInfo;
|
context 默认数据
当没有被 context 组件包裹时就会使用到默认数据
theme-context.js
1 2 3 4 5 6
| import React from "react";
const ThemeContext = React.createContext({ color: "blue", size: 10 });
export default ThemeContext;
|
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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;
|