React 之 组件间的通信

148 阅读2分钟

一. 父传子组件

  • 在父组件通过属性传递内容,
  • 子组件通过props拿到内容

举例:

父组件:

export class Main extends Component {
  constructor() {
    super()

    this.state = {
      banners: [],
      productList: []
    }
  }

  componentDidMount() {
    axios.get("http://").then(res => {
      const banners = res.data.data.banner.list
      const recommend = res.data.data.recommend.list
      this.setState({
        banners,
        productList: recommend
      })
    })
  }

  render() {
    const { banners, productList } = this.state

    return (
      <div className='main'>
        <div>Main</div>
        <MainBanner banners={banners} title="轮播图"/>
        <MainBanner/>
        <MainProductList productList={productList}/>
      </div>
    )
  }
}

子组件:

export class MainProductList extends Component {
  render() {
    const { productList } = this.props

    return (
      <div>
        <h2>商品列表</h2>
        <ul>
          {
            productList.map(item => {
              return <li key={item.acm}>{item.title}</li>
            })
          }
        </ul>
      </div>
    )
  }
}

export default MainProductList

如果子组件里还有本身设置的state数据,则需在构造函数中获取props,子组件也可以设置传入的类型验证和默认值。


export class MainBanner extends Component {
  // static defaultProps = {
  //   banners: [],
  //   title: "默认标题"
  // }

  constructor(props) {
    super(props)

    this.state = {}
  }

  render() {
    // console.log(this.props)
    const { title, banners } = this.props

    return (
      <div className='banner'>
        <h2>封装一个轮播图: {title}</h2>
        <ul>
          {
            banners.map(item => {
              return <li key={item.acm}>{item.title}</li>
            })
          }
        </ul>
      </div>
    )

    
  }
}

// MainBanner传入的props类型进行验证
MainBanner.propTypes = {
  banners: PropTypes.array,
  title: PropTypes.string
}

// MainBanner传入的props的默认值
MainBanner.defaultProps = {
  banners: [],
  title: "默认标题"
}

export default MainBanner

二. 子传父组件

  • 父组件传递函数
  • 子组件调用父组件传递的函数,传递参数给父组件
  • 父组件获取参数调用回调函数

举例:

父组件:

export class App extends Component {
  constructor() {
    super()
    this.state = {
      counter: 100
    }
  }

 changeCounter(count) {
    this.setState({ counter: this.state.counter + count })
  }

  render() {
    const { counter } = this.state

    return (
      <div>
        <h2>当前计数: {counter}</h2>
        <AddCounter addClick={(count) => this.changeCounter(count)}/>
      </div>
    )
  }
}

子组件:

export class AddCounter extends Component {

  addCount(count) {
    this.props.addClick(count)
  }
  
  render() {

    return (
      <div>
        <button onClick={e => this.addCount(1)}>+1</button>
        <button onClick={e => this.addCount(5)}>+5</button>
        <button onClick={e => this.addCount(10)}>+10</button>
      </div>
    )
  }
}

// AddCounter.propTypes = {
//   addClick: PropTypes.func
// }

export default AddCounter

三. 非父组件通信

1. 使用context

  • 创建context
  • 在要使用的组件,一般是根组件导入context
  • 使用<context.Provider>包裹后代组件
  • 在要使用的后代组件引入context
    • xxxx.contextType = context
    • 在render方法中可以通过this.context拿到传递过来的值
// 1.第一步操作 创建一个Context
const ThemeContext = React.createContext({ color: "blue", size: 10 }) //可设置默认值

export default ThemeContext

根组件App :

import React, { Component } from 'react'
import ThemeContext from "./context/theme-context"

export class App extends Component {
  constructor() {
    super()

    this.state = {
      info: { name: "kobe", age: 30 }
    }
  }

  render() {
    const { info } = this.state

    return (
      <div>
        <h2>App</h2>
        {/* 1.给Home传递数据的方式 */}
        {/* <Home name="why" age={18}/>
        <Home name={info.name} age={info.age}/>
        <Home {...info}/> */}

        {/* 2.普通的Home */}
        {/* 第二步操作: 通过ThemeContext中Provider中value属性为后代提供数据 */}
        <UserContext.Provider value={{nickname: "kobe", age: 30}}>
          <ThemeContext.Provider value={{color: "red", size: "30"}}>
            <Home {...info}/>
          </ThemeContext.Provider>
        </UserContext.Provider>
        <Profile/>
      </div>
    )
  }
}

export default App

Home组件:

export class Home extends Component {
  render() {
    const { name, age } = this.props

    return (
      <div>
        <h2>Home: {name}-{age}</h2>
        <HomeInfo/>
        <HomeBanner/>
      </div>
    )
  }
}

export default Home

Home下子组件:

类组件可以外部包裹UserContext.Consumer获取数据,也可以xxxx.contextType = context 获取

import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'

export class HomeInfo extends Component {
  render() {
    // 4.第四步操作: 获取数据, 并且使用数据
    console.log(this.context)

    return (
      <div>
        <h2>HomeInfo: {this.context.color}</h2>
        <UserContext.Consumer>
          {
            value => {
              return <h2>Info User: {value.nickname}</h2>
            }
          }
        </UserContext.Consumer>
      </div>
    )
  }
}

// 3.第三步操作: 设置组件的contextType为某一个Context
HomeInfo.contextType = ThemeContext

export default HomeInfo

函数式组件中使用Context共享的数据,必须外部包裹ThemeContext.Consumer

import ThemeContext from "./context/theme-context"

function HomeBanner() {

  return <div>
  
    <ThemeContext.Consumer>
      {
        value => {
          return <h2> Banner theme:{value.color}</h2>
        }
      }
    </ThemeContext.Consumer>
  </div>
}

export default HomeBanner

2. 使用事件总线