React 的组件通信与生命周期 (学习第二天)

本文介绍了React中组件间的通信方式,包括父子组件、跨级组件及非嵌套组件间的通信技巧。探讨了非受控与受控组件的区别,并概述了React组件的生命周期。

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

一.组件通信

React组件间通信常见的几种情况:

  • 父组件向子组件通信
  • 子组件向父组件通信
  • 跨级组件通信
  • 非嵌套关系的组件通信

React中的数据进行通信方式

  • 在React中数据是从上自下流动(传递)的,也就是一个父组件可以把它的 state / props 通过 props 传递给它的子组件,但是子组件不能修改 props

  • React.js 是单向数据流,如果子组件需要修改父组件状态(数
    据),是通过回调函数方式来完成的

1. 父子组件的通信方式?

父组件向子组件通信:父组件通过 props 向子组件传递需要的信息。

子组件向父组件通信:在父级中定义相关的数据操作方法(或其他回调), 把该方法传递给子级,在子级中调用该方法父级传递消息

// 子组件: Child
const Child = props =>{
  return <p>{props.name}</p>
}
// 父组件 Parent
const Parent = ()=>{
    return <Child name="react"></Child>
}

子组件向父组件通信:: props+回调的方式。

// 子组件: Child
const Child = props =>{
  const cb = msg =>{
      return ()=>{
          props.callback(msg)
      }
  }
  return (
      <button onClick={cb("你好!")}>你好</button>
  )
}
// 父组件 Parent
class Parent extends Component {
    callback(msg){
        console.log(msg)
    }
    render(){
        return <Child callback={this.callback.bind(this)}></Child>    
    }
}
2. 跨级组件的通信方式?

作用:就是让它所包裹的内容都可以接收到它的参数
父组件向子组件的子组件通信,向更深层子组件通信:

  • 使用props,利用中间组件层层传递,但是如果父组件结构较深,那么中间每一层组件都要去传递props,增加了复杂度,并且这些props并不是中间组件自己需要的。

  • 使用context,context相当于一个大容器,可以把要通信的内容放在这个容器中,这样不管嵌套多深,都可以随意取用,对于跨越多层的全局数据可以使用context实现。

创建一个context.js文件

import {createContext} from 'react'
// createContext是一个方法
let context=createContext()
// 它有两个返回值 一个传值(Provider)一个接收(Consumer)
let { Consumer, Provider}=context
// 导出
export default context
export {Consumer, Provider}

在父组件中传递,使用createContext中的返回值Provider传递参数

import React,{Component} from 'react'
import Childrens from './childrens'
// 需要调用 Provider 传递数据 它是一个标签
import {Provider} from './content'
export default class App extends Component{
    state={
        name:'小明',
        sex:'男'
    }
    parentValue=(newName)=>{
        this.setState({
            name:newName
        })
    }
    render(){
        let {sex}=this.state
        // </Provider>标签包裹后它里面的所有内容都能拿到它传的参数
        return (<Provider value={
           { info:'哈喽'}
        }>
            <div>    
                <p>{this.state.name}</p>
                <Childrens
                content={sex}
                parentValue={this.parentValue}
        />
        </div>
        </Provider>
        )
    }
}

在子组件使用使用createContext中的返回值Consumer接收

import React,{Component} from 'react'
import {Consumer} from './content'
export default class childrens extends Component{
    render(){
        let {Fragment}=React
        let {parentValue}=this.props
        return (<Fragment>
            <button onClick={
                ()=>{
                    parentValue('hah1')
                }
            }>点击</button>
            <p>
                {/* 把通过Provider传递过来的参数接收到 然后在页面上显示出来 */}
                <Consumer>{(value)=>value.info}</Consumer>
            </p>
        </Fragment>)
    }
}
3. 组件通信的方式有哪些

父组件向⼦组件通讯: ⽗组件可以向⼦组件通过传 props 的⽅式,向⼦组件进⾏通讯

子组件向⽗组件通讯: props+回调的⽅式,⽗组件向⼦组件传递props进⾏通讯,此props为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中

兄弟组件通信: 找到这两个兄弟节点共同的⽗节点,结合上⾯两种⽅式由⽗节点转发信息进⾏通信

跨层级通信: Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context 通信再适合不过

发布订阅模式: 发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event模块进⾏通信

全局状态管理⼯具: 借助Redux或者Mobx等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store,并根据不同的事件产⽣新的状态

二.非受控组件和受控组件

1.非受控组件

如果一个表单组件没有value props(单选和复选按钮对应的是checked props)时,就可以称为非受控组件。在非受控组件中,可以使用一个ref来从DOM获得表单值。而不是为每个状态更新编写一个事件处理程序。

类似于单项数据绑定 只可以数据改变视图

2.受控组件

在使用表单来收集用户输入时,例如等元素都要绑定一个change事件,当表单的状态发生变化,就会触发onChange事件,更新组件的state。这种组件在React中被称为受控组件,在受控组件中,组件渲染出的状态与它的value或checked属性相对应,react通过这种方式消除了组件的局部状态,使整个状态可控。react官方推荐使用受控表单组件。

受控组件更新state的流程:

可以通过初始state中设置表单的默认值
每当表单的值发生变化时,调用onChange事件处理器
事件处理器通过事件对象e拿到改变后的状态,并更新组件的state
一旦通过setState方法更新state,就会触发视图的重新渲染,完成表单组件的更新
受控组件缺陷:

表单元素的值都是由React组件进行管理,当有多个输入框,或者多个这种组件时,如果想同时获取到全部的值就必须每个都要编写事件处理函数,这会让代码看着很臃肿,所以为了解决这种情况,出现了非受控组件。

类似于 vue 的双向数据绑定 数据和视图之间可以相互影响

import React,{Component} from 'react'
//count是这个组件的名字
export default class count extends Component{
    state={
        info:'这是个值'
    }
    render(){
        let {info}=this.state
        return (
            <div>

                {/* 这里的input无论你写什么它都不会改变state中info的值 */}
                <input type='text' defaultValue={info}/>
                <input type='text' defaultValue={info}/>
                <input type='text' defaultValue={info}/>
                {/* 这里打印的一直都是上面state里面的info的值不会根据你上面input框中值变化而发生变化 */}
                <button onClick={
                    ()=>{
                        console.log(info);
                    }
                }>点击</button>
              
                <input type='text' value={info} onChange={({target})=>{
                    this.setState({
                        info:target.value
                    })
                }}/>
                {/* 这里打印的就是你input中改变的后的值 */}
                <button onClick={
                    ()=>{
                        console.log(info);
                    }
                }>受控组件</button>
            </div>
        )
    }
}

三.生命周期

生命周期流程图(新)
在这里插入图片描述

生命周期流程图(旧)
在这里插入图片描述

  • 挂载阶段
    当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

    • constructor
    • componentWillMount
    • render
    • componentDidMount
  • 更新阶段

    • 父组件更新引起组件更新
    • componentWillReceiveProps(nextProps)
    • shouldComponentUpdate(nextProps, nextState)
    • componentWillUpdate(nextProps, nextState)
    • render
    • componentDidUpdate(prevProps, prevState)
  • 组件自身更新

    • shouldComponentUpdate
    • componentWillUpdate
    • render
    • componentDidUpdate
  • 卸载阶段
    当组件从 DOM 中移除时会调用如下方法:

    • componentWillUnmount
  • 挂载阶段

    • constructor
    • static getDerivedStateFromProps(props, state)
      • 注意 this 问题
    • render
    • componentDidMount
  • 更新阶段

    • 父组件更新引起组件更新
      当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
      • static getDerivedStateFromProps(props, state)
      • shouldComponentUpdate()
      • componentWillUpdate()
      • render()
      • getSnapshotBeforeUpdate()
      • componentDidUpdate()
    • 组件自身更新
      • shouldComponentUpdate()
      • componentWillUpdate()
      • render()
      • getSnapshotBeforeUpdate()
      • componentDidUpdate()
  • 卸载阶段

  • componentWillUnmount

  • 错误处理
    当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

    • static getDerivedStateFromError()
    • componentDidCatch(error, info)
    1、constructor

    constructor 是类通用的构造函数,常用于初始化,算是生命周期的一环。React 后来的版本中类组件也可以不写。

**注意:**在构造函数中使用时,super 关键字将单独出现,并且必须在使用 this 关键字之前使用。super 关键字也可以用来调用父对象上的函数

class Test extends React.Component {
  // constructor 写法
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
    this.handleClick = this.handleClick.bind(this);
  }
  // 直接声明
  state = {
    count: 0,
  };
}
2、getDerivedStateFromProps

触发时机:state 变化、props 变化、forceUpdate,如上图。

这是一个静态方法, 是一个和组件自身"不相关"的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何组件上的数据。

// 初始化/更新时调用
static getDerivedStateFromProps(nextProps, currentState) {
  console.log(nextProps, currentState, "getDerivedStateFromProps方法执行");
  // 返回值是对currentState进行修改
  return {
    fatherText: nextProps.text,
  };
}

3、render
render 函数返回的 JSX 结构,用于描述具体的渲染内容, render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

React 元素 通常通过 JSX 创建。例如,

会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义组件,无论是

还是 均为 React 元素。

数组或 fragments 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。

Portals 可以渲染子节点到不同的 DOM 子树中。欲了解更多详细信息,请参阅有关 portals 的文档

字符串或数值类型 它们在 DOM 中会被渲染为文本节点

布尔类型或 null 什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)

注意: 如果 shouldComponentUpdate() 返回 false,则不会调用 render()。

Hooks 不需要写 render 函数。要注意的一点是,即使 Hooks 不需要写 render, 没有用到 React.xxx,组件内还是要import React from “react”;的(至于原因,后续深入 Hooks 学一下,大哥们也可以解释下)。React 官方也说了,后续的版本会优化掉这一点。

4、componentDidMount
主要用于组件加载完成时做某些操作,比如发起网络请求或者绑定事件。当做 vue 的 mounted 用就行了,这里需要注意的是:

componentDidMount() 里直接调用 setState()。它将触发额外渲染,也就是两次 render,不过问题不大,主要还是理解。

5、shouldComponentUpdate
该方法通过返回 true 或者 false 来确定是否需要触发新的渲染。因为渲染触发最后一道关卡,所以也是性能优化的必争之地。通过添加判断条件来阻止不必要的渲染。注意:首次渲染或使用 forceUpdate() 时不会调用该方法。

React 官方提供了一个通用的优化方案,也就是 PureComponent。PureComponent 的核心原理就是默认实现了 shouldComponentUpdate 函数,在这个函数中对 props 和 state 进行浅比较,用来判断是否触发更新。

当然 PureComponent 也是有缺点的,使用的时候一定要注意:由于进行的是浅比较,可能由于深层的数据不一致导致而产生错误的否定判断,从而导致页 面得不到更新。不适合使用在含有多层嵌套对象的 state 和 prop 中。

shouldComponentUpdate(nextProps, nextState) {
  // 浅比较仅比较值与引用,并不会对 Object 中的每一项值进行比较
  if (shadowEqual(nextProps, this.props) || shadowEqual(nextState, this.state) ) {
    return true
  }
  return false
}

6、getSnapshotBeforeUpdate
在 DOM 更新前被调用,返回值将作为 componentDidUpdate 的第三个参数。

getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate方法执行");

    return "componentDidUpdated的第三个参数";
}

7、componentDidUpdate
首次渲染不会执行此方法。可以使用 setState,会触发重渲染,但一定要小心使用,避免死循环

 componentDidUpdate(preProps, preState, valueFromSnapshot) {
    console.log("componentDidUpdate方法执行");

    console.log("从 getSnapshotBeforeUpdate 获取到的值是", valueFromSnapshot);
  }

8、componentWillUnmount
主要用于一些事件的解绑,资源清理等,比如取消定时器,取消订阅事件

生命周期函数详解

constructor(props)
类的构造函数,也是组件初始化函数,一般情况下,我们会在这个阶段做一些初始化的工作

  • 初始化 state
  • 处理事件绑定函数的 this

render()

  • render 方法是 Class 组件必须实现的方法
  • static getDerivedStateFromProps(props, state)

该方法会在 render 方法之前调用,无论是挂载阶段还是更新阶段,它的存在只有一个目的:让组件在 props 变化时更新 state

componentDidMount()
在组件挂载后(render 的内容插入 DOM 树中)调用。通常在这个阶段,我们可以:

  • 操作 DOM 节点
  • 发送请求

shouldComponentUpdate(nextProps, nextState)
发生在更新阶段,getDerivedStateFromProps 之后,render 之前,该函数会返回一个布尔值,决定了后续是否执行 render,首次渲染不会调用该函数

getSnapshotBeforeUpdate(prevProps, prevState)
该方法在 render() 之前,但是在输出到 DOM 之前执行,用来获取渲染之前的快照。当我们想在当前一次更新前获取上次的 DOM 状态,可以在这里进行处理,该函数的返回值将作为参数传递给下个生命周期函数componentDidUpdate

componentDidUpdate()
该函数会在 DOM 更新后立即调用,首次渲染不会调用该方法。我们可以在这个函数中对渲染后的 DOM 进行
操作

componentWillUnmount()
该方法会在组件卸载及销毁前调用,我们可以在这里做一些清理工作,如:组件内的定时器、未完成的请求等
错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法

  • static getDerivedStateFromError()
  • componentDidCatch()

四.总结

今天学习了组件通信 非受控组件 以及生命周期,和受控组件以及非受控组件
React的组件化极大的便利了我们的开发效率

"sgmediation.zip" 是一个包含 UCLA(加利福尼亚大学洛杉矶分校)开发的 sgmediation 插件的压缩包。该插件专为统计分析软件 Stata 设计,用于进行中介效应分析。在社会科学、心理学、市场营销等领域,中介效应分析是一种关键的统计方法,它帮助研究人员探究变量之间的因果关系,尤其是中间变量如何影响因变量自变量之间的关系。Stata 是一款广泛使用的统计分析软件,具备众多命令和用户编写的程序来拓展其功能,sgmediation 插件便是其中之一。它能让用户在 Stata 中轻松开展中介效应分析,无需编写复杂代码。 下载并解压 "sgmediation.zip" 后,需将解压得到的 "sgmediation" 文件移至 Stata 的 ado 目录结构中。ado(ado 目录并非“adolescent data organization”缩写,而是 Stata 的自定义命令存放目录)目录是 Stata 存放自定义命令的地方,应将文件放置于 "ado\base\s" 子目录下。这样,Stata 启动时会自动加载该目录下的所有 ado 文件,使 "sgmediation" 命令在 Stata 命令行中可用。 使用 sgmediation 插件的步骤如下:1. 安装插件:将解压后的 "sgmediation" 文件放入 Stata 的 ado 目录。如果 Stata 安装路径是 C:\Program Files\Stata\ado\base,则需将文件复制到 C:\Program Files\Stata\ado\base\s。2. 启动 Stata:打开 Stata,确保软件已更新至最新版本,以便识别新添加的 ado 文件。3. 加载插件:启动 Stata 后,在命令行输入 ado update sgmediation,以确保插件已加载并更新至最新版本。4
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值