非父子组件通信--Context

本文详细介绍了React的Context API,包括其应用场景、创建Context、Provider的使用、类组件和函数组件中如何订阅Context,以及具体的使用过程。通过Context,可以实现非父子组件间的数据共享,避免props逐层传递的冗余,特别适用于全局状态如用户信息、主题等的管理。文中还通过实例展示了Context的创建、Provider的嵌套和Consumer的使用情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Context应用场景

非父子组件数据的共享:

  • 在开发中,比较常见的数据传递方式是通过props属性自上而下(由父到子)进行传递。
  • 但是对于有一些场景:比如一些数据需要在多个组件中进行共享(地区偏好、UI主题、用户登录状态、用户信息等)。
  • 如果我们在顶层的App中定义这些信息,之后一层层传递下去,那么对于一些中间层不需要数据的组件来说,是一种冗余的操作。
import React, { Component } from 'react';

function ProfileHeader(props) {
  return (
    <div>
      <h2>用户昵称: {props.nickname}</h2>
      <h2>用户等级: {props.level}</h2>
    </div>
  )
}

class Profile extends Component {
  render() {
    return (
      <div>
        <ProfileHeader nickname={this.props.nickname} level={this.props.level} />
        <ul>
          <li>设置1</li>
          <li>设置2</li>
          <li>设置3</li>
          <li>设置4</li>
          <li>设置5</li>
        </ul>
      </div>
    )
  }
}

export default class App extends Component {
  constructor() {
    super();

    this.state = {
      nickname: "coderwhy",
      level: 99
    }
  }

  render() {
    const { nickname, level } = this.state;

    return (
      <div>
        <Profile nickname={nickname} level={level} />
        <h2>其他内容</h2>
      </div>
    )
  }
}
Spread Attributes

两种写法是等价的:

function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}

上面的Profile的传递代码可以修改为如下代码:

<ProfileHeader {...this.props}/>

但是,如果层级更多的话,一层层传递是非常麻烦,并且代码是非常冗余的:

  • React提供了一个API:Context;
  • Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props;
  • Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言;
React.createContext
const MyContext = React.createContext(defaultValue);

创建一个需要共享的Context对象:

  • 如果一个组件订阅了Context,那么这个组件会从离自身最近的那个匹配的 Provider 中读取到- 当前的context值;
  • defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值
Context.Provider
<MyContext.Provider value={/* 某个值 */}>

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化:

  • Provider 接收一个 value 属性,传递给消费组件;
  • 一个 Provider 可以和多个消费组件有对应关系;
  • 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据;

当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染;

类中的Context

Class.contextType

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
}
MyClass.contextType = MyContext;

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象:

  • 这能让你使用 this.context 来消费最近 Context 上的那个值;
  • 你可以在任何生命周期中访问到它,包括 render 函数中;
函数中的Context

Context.Consumer

<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

这里,React 组件也可以订阅到 context 变更。这能让你在 函数式组件 中完成订阅 context。

  • 这里需要 函数作为子元素(function as child)这种做法;
  • 这个函数接收当前的 context 值,返回一个 React 节点;

Context使用过程

举个栗子:

import React, { Component } from 'react';

const UserContext = React.createContext({ nickname: "默认", level: -1 })

class ProfileHeader extends Component {
  render() {
    return (
      <div>
        <h2>用户昵称: {this.context.nickname}</h2>
        <h2>用户等级: {this.context.level}</h2>
      </div>
    )
  }
}

ProfileHeader.contextType = UserContext;

class Profile extends Component {
  render() {
    return (
      <div>
        <ProfileHeader />
        <ul>
          <li>设置1</li>
          <li>设置2</li>
          <li>设置3</li>
          <li>设置4</li>
          <li>设置5</li>
        </ul>
      </div>
    )
  }
}

export default class App extends Component {
  render() {
    return (
      <div>
        <UserContext.Provider value={{ nickname: "why", level: 99 }}>
          <Profile />
        </UserContext.Provider>
        <h2>其他内容</h2>
      </div>
    )
  }
}

<Profile / >并没有作为 UserContext.Provider 的子组件:使用默认值defaultValue

<Profile />
<UserContext.Provider value={{ nickname: "why", level: 99 }}>
</UserContext.Provider>

什么时候使用Context.Consumer呢?

  • 1.当使用value的组件是一个函数式组件时;
  • 2.当组件中需要使用多个Context时;

如下栗子:

当使用value的组件是一个函数式组件时;

function ProfileHeader(props) {
  return (
    <div>
      <UserContext.Consumer>
        {value => {
          return (
            <div>
              <h2>用户昵称: {value.nickname}</h2>
              <h2>用户等级: {value.level}</h2>
            </div>
          )
        }}
      </UserContext.Consumer>
    </div>
  )
}

当组件中需要使用多个Context时;

1.创建一个新的Context

const ThemeContext = React.createContext({ color: "black" });

2.Provider的嵌套

<UserContext.Provider value={{ nickname: "why", level: 99 }}>
  <ThemeContext.Provider value={{color: "red"}}>
    <Profile />
  </ThemeContext.Provider>
</UserContext.Provider>

3.使用Consumer的嵌套

<UserContext.Consumer>
  {value => {
    return (
      <ThemeContext.Consumer>
        {
          theme => (
            <div>
              <h2 style={theme}>用户昵称: {value.nickname}</h2>
              <h2 style={theme}>用户等级: {value.level}</h2>
            </div>
          )
        }
      </ThemeContext.Consumer>
    )
  }}
</UserContext.Consumer>
### Flutter 中父子组件间的通信 在 Flutter 应用程序中,父子组件之间的通信可以通过多种方式进行优化和实现。通常情况下,父组件向子组件递数据是通过构造函数参数完成的;而子组件向父组件发送消息则可以借助回调函数来达成。 对于从父到子的数据流动: - **属性递**:利用 Dart 的命名参数特性,在创建子部件实例时指定其接收的信息作为输入字段[^4]。 ```dart class ParentWidget extends StatelessWidget { @override Widget build(BuildContext context) { return ChildWidget(data: "来自父亲的消息"); } } ``` 当涉及到由下至上即子至父方向上的交互操作时,则推荐采用如下策略之一: #### 使用回调函数机制 这是最常见也是最为直观的一种做法——让子类持有指向某个特定行为处理逻辑(通常是定义于上级作用域内的匿名内联表达式)的引用,并在其内部调用该闭包以触发相应的动作响应链路。 ```dart // 子组件声明接受一个Function类型的参数用于回掉通知给父级 typedef void MessageCallback(String message); class ChildWidget extends StatefulWidget { final String data; final MessageCallback onMessage; ChildWidget({Key key, this.data, this.onMessage}) : super(key: key); _ChildWidgetState createState() => new _ChildWidgetState(); } class _ChildWidgetState extends State<ChildWidget> { @override Widget build(BuildContext context) { return RaisedButton( onPressed: () { if (widget.onMessage != null){ widget.onMessage("子组件发出的通知"); // 当按钮被点击时执行此行代码 } }, child: Text('Click Me'), ); } } ``` 接着是在构建 `ParentWidget` 实例的过程中为上述提到的形式参数赋予具体意义: ```dart class ParentWidget extends StatefulWidget{ @override _ParentWidgetState createState()=>new _ParentWidgetState(); } class _ParentWidgetState extends State<ParentWidget>{ String receivedMsg=""; @override Widget build(BuildContext context) { return Column(children:[ Text(receivedMsg), ChildWidget(onMessage:(String msg){ setState((){receivedMsg=msg;}); }) ]); } } ``` 这种设计模式不仅使得整个系统的结构更加清晰明了易于理解,同时也便于后期维护与扩展功能模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值