react 之组件传值

3,857 阅读6分钟

前言

如果你知道 vue 组件传值的方式,其实 react 也不难理解。组件传值无非父向子传值、子向父传值、兄弟传值、跨组件传值以及提供一个状态管理容器进行传值。好啦,我要开始行动啦!

一、父组件向子组件传值

步骤

  • 父组件提供要传递的 state 数据
  • 给子组件标签添加属性,值为 state 中的数据
  • 子组件中通过 props 接收父组件中传递的数据
    • 函数组件通过参数 props 接收数据
    • 类组件通过 this.props接收数据 image.png

1.父组件把 state 中的数据作为属性值传递给子组件

App.js

import React from 'react'
// 导入子组件:函数组件和类组件
import FunCom1 from './components/FunCom1'
import FunCom2 from './components/FunCom2'
import ClassCom from './components/ClassCom'

class App extends React.Component {
  state = {
    name: 'Lucy',
    age: 18
  }
  render() {
    return <div className="App">
      app
      <FunCom1 name={this.state.name}></FunCom1>
      <hr />
      <FunCom2 msg="快快长大"></FunCom2>
      <hr />
      <ClassCom age={this.state.age}></ClassCom>
    </div>
  }
}
export default App

2.子组件通过 props/this.props 接收父组件传递过来的值

ClassCom.js

import React from 'react'
// ◆类组件:通过 this.props 接收
class ClassCom extends React.Component {
    render() {
        return <div>我是一个 类组件 ClassCom 
            <br />
            <p>爸爸说我 {this.props.age}岁了</p>
        </div>
    }
}
export default ClassCom

FunCom1.js

// ◆普通函数组件 1:通过 props 接收
function FunCom1(props) {
    return <div>我是 函数组件 FunCom1
        <br />
        <p>爸爸说我叫:{props.name}</p>
    </div>
}
export default FunCom1

FunCom2.js

// ◆箭头函数组件 2:通过 props 接收值
const FunCom2 = (props) => {
    return <div>我是 函数组件 FunCom2
        <p>爸爸说:{props.msg}</p>
    </div>
}
export default FunCom2

image.png

3.总结 props 的特点

  • 可以给组件传递任意类型的数据
  • props 是只读的,不允许修改 props 的数据
  • 注意:在类组件中使用的时候,需要把 props 传递给 super() ,否则构造函数无法获取到 props ClassCom.js子组件(类组件)
/* eslint-disable no-useless-constructor */
import React from 'react'
class ClassCom extends React.Component {
    constructor(props) {
        // ◆ 推荐将 props 传递给父类构造函数
        super(props)
    }
    render() {
        return <div>我是一个 类组件 ClassCom
            <br />
            <p>爸爸说我 {this.props.age}岁了</p>
        </div>
    }
}
export default ClassCom

二、子组件向父组件传值

步骤

  • 父组件提供一个回调函数(用于接收数据)
  • 将该函数作为属性的值,传递给子组件
  • 子组件通过 props/this.props 调用回调函数
  • 将子组件的数据作为参数传递给回调函数

1.父组件提供回调函数,作为属性值传递给子组件

App.js

import React from 'react'
// 导入子组件:函数组件和类组件
import FunCom1 from './components/FunCom1'
import FunCom2 from './components/FunCom2'
import ClassCom from './components/ClassCom'

class App extends React.Component {
  state={
    name:'',
    age:'',
    msg:'',
  }
  getFunCom1Age = (age) => {
    this.setState({
      age:age
    })
  }
  getFunCom2Name = (name) => {
    this.setState({
      name:name
    })
  }
  getClassComMsg = (msg) => {
    this.setState({
      msg:msg
    })
  }
  render() {
    return <div className="App">
      app
      <p>收到 儿子 FunCom1 的讯息: {this.state.age}</p>
      <p>收到 女儿 FunCom2 的讯息: {this.state.name}</p>
      <p>收到 儿子 ClassCom 的讯息: {this.state.msg}</p>
      <FunCom1 getAge={this.getFunCom1Age}></FunCom1>
      <hr />
      <FunCom2 getName={this.getFunCom2Name}></FunCom2>
      <hr />
      <ClassCom getMsg={this.getClassComMsg}></ClassCom>
    </div>
  }
}
export default App

2.子组件在调用父组件传递过来的函数的同时,捎带参数,就实现了子传父

FunCom1.js

// ◆子组件 FunCom1 接收函数并且调用
function FunCom1(props) {
  const clickHandler=()=>{
    props.getAge('爸爸,您今年60岁啦!')
    }
    return <div>我是 函数组件 FunCom1
        <br />
        <button onClick={clickHandler}>点我给 父组件传讯息</button>
    </div>
}
export default FunCom1

FunCom2.js

// ◆子组件 FunCom2 接收函数并且调用
const FunCom2 = (props) => {
    const clickHandler=()=>{
        props.getName('爸爸,我是 Lucy 呀')
    }
    return <div>我是 函数组件 FunCom2
        <br />
         <button onClick={clickHandler}>点我给 父组件传讯息</button>
    </div>
}
export default FunCom2

ClassCom.js

// ◆子组件 ClassCom 接收函数并且调用
import React from 'react'
class ClassCom extends React.Component {
    state={
        ClassComMsg:'爸爸,身体健康!'
    }
    clickHandler=()=>{
        this.props.getMsg(this.state.ClassComMsg)
    }
    render() {
        return <div>我是一个 类组件 ClassCom
            <br />
            <button onClick={this.clickHandler}>点我给 父组件传讯息</button>
        </div>
    }
}
export default ClassCom

image.png

子传父的核心:要想实现子传父,必先实现父传子

三、兄弟组件组件传值

即将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态

步骤:

  • 父组件提供一个回调函数(用于接收数据)
  • 将该函数作为属性的值,传递给子组件
  • 子组件通过 props/this.props 调用回调函数
  • 将子组件的数据作为参数传递给回调函数
  • 父组件在回调函数中把参数赋值给 state 中状态值
  • 父组件中把状态值 作为属性值传递给另外一个子组件
  • 另外一个子组件 通过 props/this.props 接收 image.png

1.状态提升到父组件,父组件把回调函数作为属性值传递给子组件

App.js

import React from 'react'
// 导入子组件:函数组件和类组件
import Sister from './components/Sister'
import Brother from './components/Brother'
class App extends React.Component {
  state = {
    brother: '',
    sister: '',
  }
  //◆哥哥→妹妹: 父传给哥哥,哥哥再传给父,父再传给妹妹
  brotherHandler = (msg) => {
    this.setState({
      brother: msg
    })
  }
  //◆妹妹→哥哥:父传给妹妹,妹妹再传给父,父再传给哥哥
  sisterHandler = (msg) => {
    this.setState({
      sister: msg
    })
  }
  render() {
    return <div className="App">
      app
      <Brother brotherFun={this.brotherHandler} msgfromSis={this.state.sister}></Brother>
      <hr />
      <Sister msgfromBro={this.state.brother} sisterFun={this.sisterHandler}></Sister>
      <hr />
    </div>
  }
}
export default App

2.子组件在特定时期 调用 父组件传过来的函数,传递实参给父组件,触发状态值变化

Brother.js

import React from 'react'
// 哥哥组件:向妹妹组件传消息,也接收妹妹传过来的消息
class Brother extends React.Component {
    brother=()=>{
        this.props.brotherFun('老妹,我是哥哥,给我介绍个女朋友!')
    }
    render() {
        return <div>
            编程不仅仅是技术,还是艺术!Brother
            <br />
            <p>收到妹妹传过来的消息:{this.props.msgfromSis}</p>
            <button onClick={this.brother}>按钮:点击哥哥传给妹妹</button>
        </div>
    }
}
export default Brother

3.父组件把状态值作为属性传递给另一个子组件,这就实现了兄弟组件传值

Sister.js

import React from 'react'
// ◆妹妹组件:接收哥哥组件传过来的消息,也向哥哥组件传消息
const Sister = (props) => {
  const sister = () => {
    props.sisterFun('哥哥好,我是妹妹,给我介绍个男盆友')
  }
  return <div>
    编程不仅仅是技术,还是艺术!Sister
    <p>收到哥哥的消息:{props.msgfromBro}</p>
    <button onClick={sister}>按钮:点击妹妹传哥哥</button>
  </div>
}
export default Sister

image.png

兄弟组件传值的核心思想:状态提升

四、跨组件传值

步骤

  • 调用 React. createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件。
  • 使用 Provider 组件作为父节点
  • 设置 value 属性,表示要传递的数据
  • 调用 Consumer 组件接收数据。

image.png

1.导入并调用 createContext 方法,从结果中解构出 Provider, Consumer 组件

context.js

// 1.导入 react
import React from 'react'
// Provider传,Consumer收,需要一个中间人,有点像 eventBus
// 2.导入并调用createContext方法,从结果中解构出 Provider, Consumer 组件
export const {Provider,Consumer}=React.createContext()

2.使用 Provider 组件包裹根组件,并通过 value 属性提供要共享的数据

App.js

import React from 'react'
// 导入 Parent 组件
import Parent from './components/Parent'
// 导入 Uncle 组件
import Uncle from './components/Uncle'
// ◆导入 Provider 
import { Provider } from './components/context'
class App extends React.Component {
  state={
    obj:{
      name:'Jom',
      age:20
    }
  }
  render() {
    // return (<Provider value={this.state.obj}>
    // 法2:解构
      return (<Provider 
      value={{name:this.state.obj.name,age:this.state.obj.age}}>
      <div className="App">
        app
        <Parent></Parent>
        <hr />
        <Uncle></Uncle>
      </div>
    </Provider>)
  }
}
export default App

3.在任意后代组件中,使用第1步中导出的Consumer组件包裹整个组件

Parent.js

import React from 'react'
// Parent 组件
// 导入 Son 组件
import Son from './Son'
// ◆ 导入 Consumer
import { Consumer } from './context'
class Parent extends React.Component {
    render() {
        // return (<Consumer>
        //     {(data)=><div>
        //         编程不仅仅是技术,还是艺术!Parent
        //         <p>Jom,你今年{data.age}岁了,要懂事啦!</p>
        //         <hr />
        //         <Son></Son>
        //     </div>}
        // </Consumer>)

        // 法2:解构
        return (<Consumer>
            {({age})=><div>
                编程不仅仅是技术,还是艺术!Parent
                <p>Jom,你今年{age}岁了,要懂事啦!</p>
                <hr />
                <Son></Son>
            </div>}
        </Consumer>)
    }
}
export default Parent

Uncle.js

import React from 'react'
// Uncle 组件
// ◆导入 Consumer
import { Consumer } from './context'
const Uncle = () => {
  // 这里 的 data 是形参,会自动接收Provider中传入的数据
  // return (<Consumer>
  //   {(data) => <div>
  //     编程不仅仅是技术,还是艺术!Uncle
  //     <p>{data.name}你好,听说你{data.age}岁了</p>
  //   </div>}
  // </Consumer>)

  // 法2 :解构
  return (<Consumer>
    {({name,age}) => <div>
      编程不仅仅是技术,还是艺术!Uncle
      <p>{name}你好,听说你{age}岁了</p>
    </div>}
  </Consumer>)
}
export default Uncle

Son.js

import React from 'react'
// Son 组件
// ◆ 导入 Consumer
import { Consumer } from './context'
const Son = () => {
    // 这里 的 data 是形参,会自动接收Provider中传入的数据
    // return (<Consumer>
    //     {(data) => <div>
    //         编程不仅仅是技术,还是艺术!Son
    //         <p>爷爷给我取了名字:{data.name}</p>
    //     </div>}
    // </Consumer>)

    // 法2:解构
    return (<Consumer>
        {({name}) => <div>
            编程不仅仅是技术,还是艺术!Son
            <p>我的名字是爷爷给我取的:{name}</p>
        </div>}
    </Consumer>)
}
export default Son

image.png