React组件间传值


1. 父子间传值

1.1 使用props实现父子间传值

概述:

  • 父传子:通过自定义属性向子组件传值,值可以是任何类型

  • 子传父:通过父组件把方法传给子组件,子组件调用传过去的方法完成传值

注意:

  1. 定义方法时尽可能的去使用箭头函数定义
  2. 调用方法时,尽可能的写小括号

使用:

父组件:

import React, { Component } from 'react'
import Child from './pages/Child'

class App extends Component {
  state = {
    num: 1
  }

  // 成员方法 箭头函数来定义,这样就没有this指向问题
  setNum = (n = 1) => this.setState(state => ({ num: state.num + n }))

  render() {
    let { num } = this.state
    return (
      <div>
        <Child num={num} setNum={this.setNum} />
      </div>
    )
  }
}

export default App

子组件:

import React, { Component } from 'react'

class Child extends Component {
  addNum = (evt, n = 1) => {
    n = n * 10
    this.props.setNum(n)
  }

  render() {
    let { num, setNum } = this.props
    return (
      <div>
        <h3>Child组件 -- {num}</h3>
        {/* 子去修改父传过来的数据,props它是单向数据流,子不能直接去修改 */}
        {/* 调用时,尽可能的写小括号 */}
        <button onClick={() => setNum(2)}>+++++</button>
        <button onClick={this.addNum}>+++++</button>
      </div>
    )
  }
}

export default Child

在这里插入图片描述

1.2 使用ref实现父子间传值

概述:

父组件利用 ref 对子组件做标记,通过调用子组件的方法以更改子组件的状态,也可以调用子组件的方法(说明:ref 获取当前组件对象,只针对的是使用类创建的组件才可以用此方案,类有实例,而函数没有实例,默认只针对于类组件)

使用:

父组件:

import React, { Component, createRef } from 'react'
import Child from './pages/Child'

class App extends Component {
  state = {
    title: '',
    msg: '父中的数据传给子' + Date.now()
  }

  childRef = createRef()

  render() {
    return (
      <div>
        {/* 
          父子间传值:通过ref引用对象完成对于【类组件】之间通信
          this.childRef.current 得到当前Child组件的实例对象
        */}
        <h3>{this.state.title}</h3>
        <hr />
        <Child ref={this.childRef} />
        <button
          onClick={() => {
            // 子中的数据在父中使用
            this.setState({
              title: this.childRef.current.state.title
            })
            // this.childRef.current.setState({ title: this.state.msg })
            this.childRef.current.setTitle(this.state.msg)
          }}
        >
          父组件中修改子组件中的数据
        </button>
      </div>
    )
  }
}

export default App

子组件:

import React, { Component } from 'react'

class Child extends Component {
  state = {
    title: '我是子组件'
  }

  setTitle = title => this.setState({ title })

  render() {
    return (
      <div>
        <h3>{this.state.title}</h3>
      </div>
    )
  }
}

export default Child

在这里插入图片描述

2. 状态提升

概述:

此方案用来解决兄弟组件间的传值问题,就是把共用的状态信息提升到父组件状态中。

在这里插入图片描述

使用:

import React, { Component } from 'react'

class Child1 extends Component {
  render() {
    let { setMsg } = this.props
    return (
      <div>
        Child1<input type="text" onChange={e => setMsg(e.target.value)} />
      </div>
    )
  }
}

class Child2 extends Component {
  render() {
    let { msg } = this.props
    return (
      <div>
        <h3>Child2 -- {msg}</h3>
      </div>
    )
  }
}

class App extends Component {
  state = {
    msg: ''
  }
  setMsg = msg => this.setState({ msg })

  render() {
    return (
      <div>
        <h1>App -- {this.state.msg}</h1>
        <Child1 setMsg={this.setMsg} />
        <Child2 msg={this.state.msg} />
      </div>
    )
  }
}

export default App

在这里插入图片描述

3. 跨组件通信

概述:

在 react 中没有类似 vue 中的事件总线来解决这个问题。在实际的项目中,当需要组件间跨级访问信息时,如果还使用组件层层传递 props,此时代码显得不那么优雅,甚至有些冗余。在 react 中,我们还可以使用 context 来实现跨级父子组件间的通信。

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据。

在React的 Context 中,数据我们可当成商品,发布数据的组件会用 provider 身份(卖方),接收数据的组件使用 consumer 身份(买方)

使用:

appContext(创建Context对象):

// context一个项目中可以有N个,但是一般情况下我们使用它,都是把它放在App组件中,一个项目一个
// context实现在跨组件通信,一般用于自定义组件所用
// context绑定消费是一定是一个对象

import { createContext } from 'react'

// Provider,Consumer 都是组件
// Provider 生产数据的组件
// Consumer 消费的组件

// 导出写法1
// const ctx = createContext()
// export const { Provider, Consumer } = ctx
// export default ctx


// 导出写法2
const ctx = createContext()
const { Provider, Consumer } = ctx
export {
  ctx as default,//默认导出
  Provider,//按需导出
  Consumer//按需导出
}

父组件(发布消息):

import React, { Component } from 'react'
import Child from './pages/Child'

// 在App组件中生产数据,这样下面的子孙组件就可以调用此context对象来消费数据
import { Provider } from './context/appContext'

class App extends Component {
  state = {
    name: '张三',
    setName: name => this.setState({ name })
  }

  render() {
    return (
      <div>
        {/* value属性就是生产的数据源 */}
        <Provider value={this.state}>
          <Child />
        </Provider>
      </div>
    )
  }
}

export default App

子组件(组件消费):

import React, { Component } from 'react'

// 假设现在当前组件是App组件下面的很深的子孙组件
import ctx, { Consumer } from '../../context/appContext'

class Child extends Component {
  // 类组件中context还可以换一个方案来消费,定义好后,就可以通过成员属性 this.context来获取数据
  static contextType = ctx

  render() {
    return (
      <div>
        {/* 消费方案1 */}
        {/* <Consumer>{state => <h3>{state.name}</h3>}</Consumer> */}
        {/* 消费方案2(常用方案) */}
        <h3>{this.context.name}</h3>
        <button
          onClick={() => {
            // 这里更改的是父组件中数据源的数据,父组件一改,父组件下所有的子孙组件都会更新
            this.context.setName(Date.now())
          }}
        >
          ++++
        </button>
      </div>
    )
  }
}

export default Child

在这里插入图片描述

4. 状态提升案例:toDoList

父组件(App.jsx):

import React, { Component } from 'react'
import Todo from './todo'
import './app.css'

class App extends Component {
  render() {
    return (
      <div>
        <h3 className="app-title">App组件</h3>
        <Todo />
      </div>
    )
  }
}

export default App

子组件(index.jsx):

import React, { Component } from 'react'
import Form from './Form'
import List from './List'

class Todo extends Component {
  state = {
    todos: []
  }

  addTodo = todo => this.setState(state => ({ todos: state.todos.concat(todo) }))

  render() {
    let { todos } = this.state
    return (
      <div>
        <Form addTodo={this.addTodo} />
        <List todos={todos} />
      </div>
    )
  }
}

export default Todo

子组件(form.jsx):

import React, { Component } from 'react'

class Form extends Component {
  onEnter = e => {
    if (e.key === 'Enter') {
      let value = e.target.value.trim()
      e.target.value = ''
      this.props.addTodo({
        id: Date.now(),
        title: value,
        done: false
      })
    }
  }

  render() {
    return (
      <div>
        <input type="text" onKeyUp={this.onEnter} />
      </div>
    )
  }
}

export default Form

子组件(list.jsx):

import React, { Component } from 'react'
import style from './style.module.css'

class List extends Component {
  render() {
    let { todos } = this.props
    return (
      <div>
        <ul>
          {todos.map(({ id, title, done }) => (
            <li key={id}>
              <span className={done ? style.title : ''}>{title}</span>
              <span>删除</span>
            </li>
          ))}
        </ul>
      </div>
    )
  }
}

export default List

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值