基础知识梳理 ~ react组件通信

1 父子通信

  • 在父组件中的子组件标签上将父组件的变量,方法通过属性的形式让子组件可以访问

  • 在子组件中通过this.props.属性名访问对应变量或方法

  • 方法应定义为箭头函数,避免不同组件调用方法导致this指向混乱

    // 以todos功能为例
    
    // 父组件
    import React, { Component } from 'react'
    import AddItem from './AddItem'
    import Todolist from './Todolist'
    
    class Content extends Component {
      constructor(props) {
        super(props)
        this.state = {//父组件中的变量
          value: '',
          arrList: []
        }
      }
      // 定义方法
      // 用setState函数修改state中变量,避免直接对state中的变量进行操作
      // 为了子组件调用父组件方法时不改变this指向,应将方法定义为箭头函数
      handleChange = (value) => {
        this.setState({//异步操作,但若在定时器中使用就是同步操作
          value
        })
      }
      handleClick = () => {
        this.setState({
          arrList: [...this.state.arrList, this.state.value],
          value: ''
        })
      }
      deleteClick = (id) => {
        let temparr = this.state.arrList
        temparr.splice(id,1)
        this.setState({
          arrList: temparr
        })
      }
      render() {
        return (
          <div>
            <AddItem value="{this.state.value}" handleChange="{this.handleChange}" handleClick="{this.handleClick}"/>
            <Todolist arrList="{this.state.arrList}" deleteClick="{this.deleteClick}"/>
          </div>
        )
      }
    }
    
    export default Content
    
    // 子组件 AddItem
    import React, { Component, Fragment } from 'react'
    
    class AddItem extends Component {
      getInpValue = (e) => {
        this.props.handleChange(e.target.value)
      }
      render() {
        return (
          <Fragment>
            <input type="text" value="{this.props.value}" onChange="{this.getInpValue}"/>
            <button onClick="{this.props.handleClick}">添加任务</button>
          </Fragment>
        )
      }
    }
    
    export default AddItem
    
    // 子组件 Todolist
    import React, { Component } from 'react'
    
    class Todolist extends Component {
      // 使用父组件方法需要传参,应在子组件方法中调用,通过bind绑定参数给子组件方法传参
      // 虽然父组件定义方法时箭头函数时,子组件给该方法bind绑定参数也可以,但以上方法更可靠
      deleteItem (i) {
        this.props.deleteClick(i)
      }
      render() {
        return (
          <ul>
            {
              this.props.arrList.map((item,index) => {
                return (
                  <li key="{index}">
                    {item}
                    <span onClick="{this.props.deleteClick.bind(this,index)}">X</span>
                  </li>
                )
              })
            }
          </ul>
        )
      }
    }
    
    export default Todolist
    

2 利用context跨组件通信

  • context的功能

    • 在react没有类似vue中的事件总线来解决这个问题
    • 我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系
    • react提供了context api来实现跨组件通信, React 16.3之后的contextapi较之前的好用
  • 使用方法

    • React.createContext() 我们需要用到createContext创建上下文

    • 返回的是一个对象 对象内有2个组件

      • Provider:设置共享状态及方法
        • 通过value去传递
      • Consumer:接收共享状态或方法
        • 只要被 Provider这个组件包裹的所有组件都可以通过Consumer接收
        • Consumer 组件 内部必须返回一个函数,这个函数会接受到 Provider传递过来的状态及方法
    • 示例:按钮实现加/减

      • index.js
      // index.js
      import React from 'react';
      import ReactDOM from 'react-dom';
      import App from './App';
      import CountProvider from './context'
      // 组件标签内就算有内容也不会自动去解析,需在组件内部通过this.props.children获取使用
      ReactDOM.render(
        (
          <CountProvider>
            <App/>
          </CountProvider>
        ),
        document.getElementById('root'));
      
      • context.js

        import React, { createContext, Component, Fragment } from 'react'
        // Fragment不会生成DOM标签,一般用来做根标签,也可以用<></>代替
        const {Consumer, Provider} = createContext()
        
        class CountProvider extends Component {
          constructor(props) {
            super(props)
            this.state = {
              num: 10
            }
          }
          addNum = () => {
            this.setState({
              num: this.state.num + 1
            })
          }
          minusNum = () => {
            this.setState({
              num: this.state.num - 1
            })
          }
          // 此处this.props.children获取到的是APP组件,故在App组件中使用的组件都可以可以通过Consumer接收
          render() {
            return (
              <Fragment>
                <Provider value="{{" num:="" this.state.num,="" addNum:="" this.addNum,="" minusNum:="" this.minusNum="" }}="">
                  {this.props.children}
                </Provider>
              </Fragment>
            )
          }
        }
        // export 导出语法有
            // export let/const 变量名
            // 或export function 函数名 () {}
            // 或export {变量名}
        // export default 可直接导出 变量,但只能使用一次
        export default CountProvider
        export {Consumer, Provider}
        
      • App.js

        import React, { Component } from 'react'
        import CountButton from './components/button'
        import Count from './components/count'
        
        class App extends Component {
          render () {
            return (
              <div>
                <CountButton type="add">+</CountButton>
                <Count/>
                <CountButton type="minus">-</CountButton>
              </div>
            )
          }
        }
        
        export default App;
        
      • /count/index.js

        import React, { Component } from "react";
        import {Consumer} from '../../context'
        
        class Count extends Component {
          render() {
            return (
              <Consumer>
                {
                  ({num}) => {
                    return (
                      <span>{num}</span>
                    )
                  }
                }
              </Consumer>
            )
          }
        }
        
        export default Count
        
      • /button/index.js

        import React, { Component } from 'react'
        import { Consumer } from '../../context'
        
        class CountButton extends Component {
          render() {
            return (
              <Consumer>
                {
                  ({addNum, minusNum})=>{
                    const setFunc = this.props.type === 'add' ? addNum : minusNum
                    return (
                      <button onClick="{setFunc}">
                        {this.props.children}
                      </button>
                    )
                  }
                }
              </Consumer>
            )
          }
        }
        
        export default CountButton
        

3 ref通信

  • 适用场景

    • 对DOM 元素焦点的控制、内容选择或者媒体播放
    • 通过对DOM元素控制,触发动画特效
    • 集成第三方 DOM 库
  • 避免使用

    • 避免使用 refs 去做任何可以通过声明式实现来完成的事情
    • 例如,避免在Dialog、Loading、Alert等组件内部暴露 open(), show(), hide(),close()等方法,最好通过 isXX属性的方式来控制
  • 使用限制

    • 函数组件内部不支持使用 字符串 refs (支持 createRef 、 useRef 、 回调 Ref)
    • 不能在函数组件上使用 ref属性,因为函数组件没有实例
    • useRef 仅限于在函数组件内使用

3.1 String类型的refs

  • 在元素或组件标签上设置ref属性

    <button ref="btnRef" onClick="{this.handleClick}">dianwo</button>
    <Son ref="sonRef"/>
    
  • 通过this.refs得到所有设置了ref属性的元素或组件实例

    • 如通过this.refs.sonRef可访问Son组件中的状态和方法
    • 通过this.refs.btnRef操作真实button DOM
  • 过时的API,官方建议使用 回调函数 或者 createRef API的方式来替换

  • 函数组件内部不支持使用

3.2 createRef API

  • 在React16.3 版本中引入的

  • 使用 React.createRef()创建 Refs,并通过 ref属性附加至 React元素上。通常在构造函数中,将 Refs分配给实例属性,以便在整个组件中引用。

  • ref被传递给 render中的元素时,对该节点的引用可以在 refcurrent属性中访问

  • ref的值根据节点的类型而有所不同

  • ref属性用于 HTML元素时,构造函数中使用 React.createRef()创建的 ref接收底层 DOM元素作为其 current属性

  • ref属性用于自定义的 class 组件时, ref对象接收组件的挂载实例作为其 current属性

  • 不能在函数组件上使用 ref属性,因为函数组件没有实例

this.sonRef = createRef()
<Son ref="{this.sonRef}"/>
this.sonRef.current

3.3 useRef API

  • useRef是 React16.8 中引入的,只能在函数组件中使用

  • 使用 React.useRef()创建 Refs,并通过 ref属性附加至 React元素上

  • 返回的 ref 对象在组件的整个生命周期内保持不变

  • ref被传递给 React 元素时,对该节点的引用可以在 refcurrent属性中访问

import React from 'react';

export default function MyInput(props) {
    const inputRef = React.useRef(null);
    React.useEffect(() => {
        inputRef.current.focus();
    });
    return (
        <input type="text" ref="{inputRef}"/>
    )
}

3.4 回调形式的refs

  • 在react较早的版本中,我们推荐使用 回调形式的refs

  • 这种方式可以帮助我们更精细的控制何时 Refs 被设置和解除

  • 将回调函数传递给 React元素ref属性。这个函数接受 React 组件实例 或 HTML DOM 元素作为参数,将其挂载到实例属性上

  • React 会在组件挂载时,调用 ref回调函数并传入 DOM元素(或React实例),当卸载时调用它并传入 null。在 componentDidMountcomponentDidUpdate触发前,React 会保证 Refs 一定是最新的

import React from 'react';

export default class MyInput extends React.Component {
    constructor(props) {
        super(props);
        this.inputRef = null;
        this.setTextInputRef = (ele) => {
            this.inputRef = ele;
        }
    }

    componentDidMount() {
        this.inputRef && this.inputRef.focus();
    }
    render() {
        return (
            <input type="text" ref="{this.setTextInputRef}/">
        )
    }
}

参考: 你想知道的关于 Refs 的知识都在这了 ---刘小夕

基础决定未来,一步一个脚印

原文链接:juejin.im

上一篇:2020年关于 Vue 3 你需要知道的事
下一篇:Pointer Events API 介绍

相关推荐

  • 🍊仿Element自定义Vue组件库

    (/public/upload/643b972fb2ebd2e6272ff8b16712b205) 前言 🍊 市面上目前已有各种各样的UI组件库,他们的强大毋庸置疑。

    24 天前
  • 🆘 一次理解清楚,为什么使用 React useEffect 中使用 setInterval 获取的值不是最新的

    Intro 这篇文章将通过一个使用 React Hook 常遇到的问题(stale state)入手,尝试理解 Hook 的内部运行逻辑。 废话不多说,直接看示例Sandbox。

    1 个月前
  • (vue框架)为element组件赋初始值以后无法更改值得问题

    情况描述:组件未加载时已有初始值,mounted里面加载数据,赋值,渲染以后,组件无法更改内容 data里面已经有这个表单对象的初始值但还是无法修改,之前有过一次,没有给表单绑定对象,所以赋值以后无法...

    1 年前
  • 魔方实时通信一对一音视频组件

    魔方实时通信/协作引擎(Web SDK)是一个全能力的实时云端协作引擎 魔方实时通信,请点击这个(https://www.shixincube.com/) 继上一个im聊天组件增加了发动语音,语音...

    2 年前
  • 高频数据交换下Flutter与ReactNative的对比

    (标题图片来自网络,侵删) 后端使用go写的socketio服务模拟期货行情数据,每10ms推送10条行情数据 ReactNative已经尽力优化了。 Flutter由于没fluttersock...

    2 年前
  • 高阶组件之属性代理

    新组件类继承子React.component类,对传入的组件进行一系列操作,从而产生一个新的组件,达到增强组件的作用 操作props 访问ref 抽取state 封装组件 ...

    1 年前
  • 高阶组件HOC - 小试牛刀

    1. 前言 老毕曾经有过一句名言,叫作“国庆七天乐,Coding最快乐~”。所以在这漫漫七天长假,手痒了怎么办?于是乎,就有了接下来的内容。。。 2. 一个中心 今天要分享的内容有关高阶组件...

    2 年前
  • 高阶组件 + New Context API = ?

    1. 前言 继上次小试牛刀(https://github.com/SmallStoneSK/Blog/issues/6)尝到高价组件的甜头之后,现已深陷其中无法自拔。。。

    2 年前
  • 高阶函数&amp;&amp;高阶组件

    高阶函数 特点: 接受函数类型的参数。 返回值是函数。 高阶函数具有可扩展性。 常见的高阶函数: 定时器 setTimeout(); setInterval() Promise(); 数组相关:...

    5 个月前
  • 高级 Angular 组件模式 (3b)

    03b Enhance Components with Directives 原文: Enhance Components with Directives(https://blog.angula...

    3 年前

官方社区

扫码加入 JavaScript 社区