2

react之组件通信

 2 years ago
source link: https://segmentfault.com/a/1190000040305644
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

最新在做react的B端项目、因为项目中没有用到Redux等数据管理库、所以涉及到了很多组件之前的数据传递和嵌套组件如何传递的方式,温故而知新 特此整理记录一下。

常见的组建通信的方式:

  • 父子组件通信
  • 子父组件通信
  • 兄弟组件通信
  • 爷孙通信(嵌套组件)
  • 等等

代码环境:

react: ^16.13.0

react-dom:^16.8.2

父子组件通信:

通常来说是父组件通过props、refs等方式来调用子组件的方法和属性等;

props: 子组件接受父组件的值进行视图更新

class 组件:

import React, { Component } from 'react'

// 子组件
class Child extends Component {
  constructor(props) {
    super(props)
    this.state = {}
  }

  render() {
    const { mun } = this.props;
    return (
      <>
        这里是child子组件:
        <p>{ mun }</p>
      </>
    )
  }
}

// 父组件
class Parent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mun:1
    }
  }

  handleParent = () => {
    this.setState({
      mun: this.state.mun + 1
    })
  }

  render() {
    const { mun } = this.state
    return (
      <>
        <button onClick={this.handleParent}>我是父组件的按钮。你可以点击我</button>
        <br />

        <Child mun={mun} />
      </>
    )
  }
}

export default Parent

Hooks组件:

import React, { useState } from 'react'
const Child = (props) => {

  const { mun } = props;
  return (
    <>
      这里是child子组件:
      <p>{ mun }</p>
    </>
  )
}


const Parent = () =>{
  const [mun, setMun] = useState(0)

  const handleParent = () => {
    setMun((value) => {
      return value + 1
    })
  }

  return (
    <>
      <button onClick={handleParent}>我是父组件的按钮。你可以点击我</button>
      <br />

      <Child mun={mun} />
    </>
  )
}

export default Parent

refs:父组件通过声明ref拿到子组件的对象、进行操作子组件的对象方法

class 组件:

import React, { Component, createRef } from 'react'

// 子组件
class Child extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mun:1
    }
  }

  // 子组件累加累加
  accumulation = () => {
    this.setState({
      mun: this.state.mun + 1
    })
  } 

  render() {
    const { mun } = this.state
    return (
      <>
        这里是child子组件:
        <p>{ mun }</p>
      </>
    )
  }
}


// 父组件
class Parent extends Component {
  constructor(props) {
    super(props)

    // 通过ref控制
    this.createRef = createRef()
    this.state = {}
  }

  handleParent = () => {
    this.createRef && this.createRef.current.accumulation()
  }

  render() {
    return (
      <>
        <button onClick={this.handleParent}>我是父组件的按钮。你可以点击我</button>
        <br />

        <Child ref={this.createRef} />
      </>
    )
  }
}

export default Parent

Hooks 组件:

注意:在hooks中需要通过forwardRef转发获取ref的值,然后通过useImperativeHandle向父组件抛出子组件的方法。

import React, { useRef, useState, useImperativeHandle, forwardRef } from 'react'

const Child = forwardRef((_, ref) => {

  const [mun, setMun] = useState(0)

  // 累加方法
  const accumulation = () => {
    setMun((value) => {
      return value + 1
    })
  }
  
  // 给父组件抛出方法
  useImperativeHandle(ref, () => ({
    accumulation,
  }), [accumulation]);

  return (
    <>
      这里是child子组件:
      <p>{ mun }</p>
    </>
  )
})

const Parent = () =>{
  const ChildRef = useRef(null);

  const handleParent = () => {
    ChildRef.current && ChildRef.current.accumulation()
  }

  return (
    <>
      <button onClick={handleParent}>我是父组件的按钮。你可以点击我</button>
      <br />

      <Child ref={ChildRef} />
    </>
  )
}

export default Parent

子父组件通信:

子组件通过CallBack回调函数的方式来拿到父组件的方法或者属性等

Class 组件:

import React, { Component, createRef } from 'react'

// 子组件
class Child extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mun:0
    }
  }

  // 子组件累加累加
  accumulation = () => {
    const { handleParent } = this.props
    const { mun } = this.state

    this.setState({
      mun:this.state.mun + 1
    },() => {
      handleParent(this.state.mun)
    })
  } 

  render() {
    const  { mun } = this.props
    return (
      <>
        这里是child子组件:
        {/* <p>{ mun }</p> */}
        <button onClick={this.accumulation}>我是子组件的按钮、可以点击我</button>
      </>
    )
  }
}


// 父组件
class Parent extends Component {
  constructor(props) {
    super(props)

    // 通过ref控制
    this.ChildRef = createRef()
    this.state = {
      getChildmun:0
    }
  }

  handleParent = (_mun) => {
    this.setState({
      getChildmun:_mun
    })
  }

  render() {
    const { getChildmun } = this.state
    return (
      <>
        <h2>我是父组件</h2>
        <p>子组件控制的值:{ getChildmun }</p>
        <br />
        <Child handleParent={this.handleParent} />
      </>
    )
  }
}

export default Parent

Hooks 组件:

import React, { useState, useEffect } from 'react'

const Child = (props) => {
  const { handleParent } = props;
  const [mun,setMun] = useState(0)

   // 子组件累加累加
  const accumulation = () => {
    setMun((value) => {
      return value + 1
    })
  } 

  useEffect(() => {
    if(mun !== 0) {
      handleParent(mun)
    }
  }, [mun]);

  return (
    <>
      这里是child子组件:
      <button onClick={accumulation}>我是子组件的按钮、可以点击我</button>
    </>
  )
}



const Parent = () =>{
  const [getChildmun,setChildmun] = useState(0);

  return (
    <>
        <h2>我是父组件</h2>
        <p>子组件控制的值:{ getChildmun }</p>
        <br />
        <Child handleParent={(mun) => setChildmun(mun)} />
    </>
  )
}

export default Parent

兄弟组件通信:

兄弟组件通信一般都是在一个父组件下平级的两个兄弟组件、利用父组件当中介进行传递

Class 组件:

import React, { Component, createRef } from 'react'

// 子组件1
class Child1 extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mun:0
    }
  }

  render() {
    const { brother } = this.props

    return (
      <>
        <h2>我是Child1组件:{brother}</h2>
      </>
    )
  }
}

// 子组件1
class Child2 extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mun:0
    }
  }

  // 改变自己的值、通过父组件传递
  accumulation = () => {
  const { brotherCallback } = this.props
    
   this.setState({
    mun:this.state.mun + 1
   }, () => {
    brotherCallback(this.state.mun)
   })
  } 

  render() {
    const  { mun } = this.props
    return (
      <>
        <h2>我是Child2组件:</h2>
        <button onClick={ this.accumulation }>点击我 改变[Child1]组件的值</button>
      </>
    )
  }
}

// 父组件
class Parent extends Component {
  constructor(props) {
    super(props)

    // 通过ref控制
    this.ChildRef = createRef()
    this.state = {
      brother:0
    }
  }

  // 利用中介的方式
  brotherCallback = (mun) => {
    this.setState({
      brother:mun
    })
  }

  render() {
    const { brother } = this.state 
    return (
      <>
        <h2>我是父组件</h2>
        <hr />
        <Child1 brother={brother} />
        <hr />
        <Child2 brotherCallback={this.brotherCallback}/>
      </>
    )
  }
}

export default Parent

Hooks 组件:

import React, { useState,useEffect } from 'react'

const Child1 = (props) => {
  const { brother } = props;
  return (
    <>
       <h2>我是Child1组件:{brother}</h2>
    </>
  )
}

const Child2 = (props) => {
  const [mun, setMun] = useState(0)
  const { brotherCallback } = props

  // 改变自己的值、通过父组件传递
  const accumulation = () => {
    setMun((value) => {
      return value + 1
    })
  } 

  useEffect(() => {
   if(mun !== 0 ) {
    brotherCallback(mun)
   }
  }, [mun])

  return (
    <>
      <h2>我是Child2组件:</h2>
      <button onClick={accumulation }>点击我 改变[Child1]组件的值</button>
    </>
  )
}

const Parent = () => {
  const [brother,setBrother] = useState(0);
  return (
    <>
       <h2>我是父组件</h2>
        <hr />
        <Child1 brother={brother} />
        <hr />
        <Child2 brotherCallback={(mun) => setBrother(mun)}/>
    </>
  )
}

export default Parent

(爷孙组件)嵌套组件:

嵌套组件其实也可以采用父子组件的传递方式、但是如果层级过深显然不好维护,所以这里推荐采用Context进行维护数据源的状态

详情请看:Context文档描述的很详细。

Class 组件:

import React, { Component, createRef } from 'react'

// 后期可以把他单独放在一个js文件维护
const ThemeContext = React.createContext();

// 孙子组件
class Grandson extends Component{
  constructor(props) {
    super(props)
    this.state = {}
  }

  static contextType = ThemeContext;
  
  shouting = () => {
    const { grandsonCallback } = this.context
    grandsonCallback({
      massge:"我累了、毁灭吧"
    })
  }

  render() {
    const { parentObj } = this.context
    return (
      <>
       <h2>我是孙子组件</h2>
       <p>接收的值:{parentObj.name} 年龄{parentObj.age}岁</p>
       <button onClick={this.shouting}>隔空喊话</button>
      </>
    )
  }
}

// 儿子组件
class Child extends Component {
  constructor(props) {
    super(props)
    this.state = {}
  }

  render() {
    return (
      <>
       <h2>我是儿子组件</h2>
       <hr />
       <Grandson />
      </>
    )
  }
}

// 父组件
class Parent extends Component {
  constructor(props) {
    super(props)

    this.state = {
      parentObj:{
        name:"我是你爷爷",
        age:88
      },
      // 接受孙子组件的数据
      grandson:{}
    }
  }

  grandsonCallback = (data) => {
    this.setState({
      grandson:data
    })
  }
  render() {
    const { parentObj, grandson } = this.state;
    return (
      <>
        <h2>我是父组件</h2>
        <p>{( grandson && grandson.massge ) ? grandson.massge : '' }</p>
        <hr />
        <ThemeContext.Provider value={ { parentObj, grandsonCallback: this.grandsonCallback } }>
          <Child />
        </ThemeContext.Provider>
      </>
    )
  }
}

export default Parent

Hooks 组件:

import React, { useState, useContext } from 'react'

// 后期可以把他单独放在一个js文件维护
const ThemeContext = React.createContext();

const Grandson = () => {
  const context = useContext(ThemeContext)
  const { parentObj, grandsonCallback } = context;

  const shouting = () => {
    grandsonCallback({
      massge:"我累了、毁灭吧"
    })
  }

  return (
    <>
     <h2>我是孙子组件</h2>
     <p>接收的值:{parentObj.name} 年龄{parentObj.age}岁</p>
     <button onClick={shouting}>隔空喊话</button>
    </>
  )
}

const Child = () => {
  return (
    <>
      <h2>我是儿子组件</h2>
      <hr />
      <Grandson />
    </>
  )
}

const Parent = () =>{
  const [grandson, setGrandson] = useState({});
  const [parentObj, setParentObj] = useState(
  {
    name:"我是你爷爷",
    age:88
  });

  const grandsonCallback = (data) => {
    setGrandson(data)
  }

  return (
    <>
      <h2>我是父组件</h2>
      <p>{( grandson && grandson.massge ) ? grandson.massge : '' }</p>
      <hr />
      <ThemeContext.Provider value={ { parentObj, grandsonCallback } }>
        <Child />
      </ThemeContext.Provider>
    </>
  )
}

export default Parent

最后

     其实在常见的组件通信中,以上的方式绰绰有余、但是面对复杂的大型项目中还是推荐采用redux这样的状态存储对象来统一管理、如果redux灵活性不高,这里也推荐采用dva进行管理、就像它的特性一样:快速上手、易学易用。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK