React--组件间通信

父传子props

  • 父组件通过 属性 = 值 的形式来传递给子组件数据
jsx 复制代码
 const banners = ['新歌曲', '新MV', '新歌单']
 <MainBanner banners={banners}/>
  • 子组件通过 props 参数获取父组件传递过来的数据,会自动保存到 this.props
jsx 复制代码
 // 类组件使用props
 class MainBanner extends Component {
   render() {
     return (
       <div>
         <ul>
           {this.props.banners.map(item=>{
             return <li key={item}>{item}</li>
           })}
         </ul>
       </div>
     )
   }
 }
 ​
 // 函数组件使用props
 function MainBanner(props) {
   return (
       <div>
         <ul>
           {props.banners.map(item=>{
             return <li key={item}>{item}</li>
           })}
         </ul>
       </div>
     )
 }
  • 使用 propTypes 对传入的数据进行类型限制
javascript 复制代码
 // MainBanner.jsx
 import PropTypes from 'prop-types'
 ​
 class MainBanner extends Component {
   static propTypes = {
     // 限制传入的数据必须是array类型
     banners: PropTypes.array
     // 限制传入的数据是string且必传
     title: PropTypes.string.isRequired
   }
 }
  • props 使用默认值
javascript 复制代码
 class MainBanner extends Component {
   static defaultProps = {
     banners: [],
     title: '轮播图'
   }
 }

子传父触发函数

  • 同样通过 props 父组件给子组件传递一个回调函数,在子组件中调用这个函数即可
  • 在父组件中向子组件传递函数
jsx 复制代码
 import { Component } from 'react'
 import Count from './components/Count'
 ​
 class App extends Component {
   constructor(){
     super()
     this.state = {
       count: 100
     }
   }
 ​
   handleCountAdd(count) {
     this.setState({
       count: this.state.count + count
     })
   }
   
   render() {
     const { count } = this.state
     return (
       <div className='app'>
         <h1>当前计数:{count}</h1>
         {/* 父组件传递函数到子组件 */}
         <Count countAddClick={(count) => this.handleCountAdd(count)}/>
       </div>
     )
   }
 }
  • 在子组件接收该函数,并调用
jsx 复制代码
 import { Component } from 'react'
 import propsTypes from 'prop-types'
 ​
 class Count extends Component {
   // 子组件接收函数类型传递
   static propsTypes = {
     countAddClick: propsTypes.func
   }
 ​
   render() {
     // 拿到父组件的函数进行调用
     const { countAddClick } = this.props
     return (
       <div>
         <button onClick={e=>countAddClick(1)}>+1</button>
         <button onClick={e=>countAddClick(5)}>+5</button>
         <button onClick={e=>countAddClick(10)}>+10</button>
       </div>
     )
   }
 }

兄弟互传事件总线

  • 对于兄弟组件间的通信,可以使用 eventBus 发送和监听事件实现
  • React 中需要安装一个第三方库 events,用于创建全局的 eventBus
shell 复制代码
 npm i events
  • 创建 eventBus.js 文件,用于创建一个 eventBus 实例
javascript 复制代码
 import { EventEmitter } from 'events'
 ​
 const eventBus = new EventEmitter()
 ​
 export default eventBus
  • 在需要发送事件的组件中,使用 eventBus.emit(事件名, 发送的数据) 发送事件
javascript 复制代码
 import eventBus from './utils/eventBus'
 ​
 eventBus.emit('click', { name: 'tony', age: 18 })
  • 在需要监听事件的组件中,使用 eventBus.on(事件名, 回调函数) 实现监听,并在回调函数中编写逻辑
javascript 复制代码
 import eventBus from './utils/eventBus'
 ​
 // 在组件挂载完成后监听事件
 componentDidMount() {
   eventBus.on('click', this.onClickCallBack.bind(this))
   // 注意这里的回调函数需要显示绑定this,否则回调函数中的this为undefined
 }
 ​
 onClickCallBack(payload) {
   console.log('监听click事件', payload); // {name: 'tony', age: 18}
 }
  • 在组件销毁时,需要同时将监听的事件关闭,使用 eventBus.on(事件名, 回调函数)
javascript 复制代码
 componentWillUnmount() {
   eventBus.off('click', this.onClickCallBack)
 }

深层组件数据共享

  • 对于更深层组件而言,如果在顶层组件定义数据,然后逐层传递下去,对于一些中间层不需要数据的组件来说是一种冗余的操作
  • Vue 中提供了 provice / inject 解决深层传递数据的现象,而 React 提供了称为 ContextAPI

Context 的作用和目的

  • 作用: 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props
  • 目的: 为了共享那些对于组件树而言是"全局"的数据

React.createContext

  • createContext 用于创建一个需要共享的 Context 对象,接收一个参数 defaultValue
  • defaultValue 是组件在顶层查找过程中未找到对应的 Provider 所使用的默认值
javascript 复制代码
 React.createContext(defaultValue)
  • 首先新建一个 context.js 文件,在里面创建一个全局共享的 Context
javascript 复制代码
 import { createContext } from 'react'
 ​
 const ThemeContext = createContext()
 ​
 export default ThemeContext

Provider

  • 每个 Context 对象会提供一个 Provider 组件,用于向其后代组件提供共享的数据,并且 Provider 组件接收一个 value 属性,值是传递给后代组件的数据
  • 多个 Provider 可嵌套使用,里层的会覆盖外层相同的数据
  • Providervalue 值发生变化时,其内部所有后代组件都会重新渲染
  • 若某个后代组件订阅了 Context,该组件会从离自身最近的 Provider 中读取当前提供的数据
jsx 复制代码
 import ThemeContext from './context'
 class App extends Component {
   render() {
     return (
       <div>
         {/* 后代组件须包裹在其Provider子组件中,寓意提供者 */}
         {/* value是需要提供的数据 */}
         <ThemeContext.Provider value={{ color:'red', size:30 }}>
           <Home/>
         </ThemeContext.Provider>
       </div>
     )
   }
 }

contextType

  • 类组件实例中有一个静态属性 contextType ,需赋值为当前创建的 Context对象
  • 使用 this.context 来消费最近 Provider 提供的数据
jsx 复制代码
 import ThemeContext from './context'
 // HomeInfo是Home的子组件
 class HomeInfo extends Component {
   // 对挂载在类上的contextType重新赋值
   static contextType = ThemeContext
   render() {
     // 获取最近Context.Provider中的数据
     const { color, size } = this.context
     return (
       <div>
         <h1>color:{color}</h1>
         <h1>size:{size}</h1>
       </div>
     )
   }
 }

Consumer

  • 函数式组件没有 this.context,也没有 contextType 属性,需使用其提供的 Consumer 子组件包裹
  • 并且使用函数作为子元素,函数的参数则是 Provide 提供的数据
jsx 复制代码
 function HomeInfo() {
   return (
     <div>
       {/* 使用Consumer子组件包裹,寓意消费者 */}
       <ThemeContext.Consumer>
         {
           {/* 函数作为子元素 */}
           ({color, size}) => (
             <div>
               <h2>color:{color}</h2>
               <h2>size:{size}</h2>
             </div>
           )
         }
       </ThemeContext.Consumer>
     </div>
   )
 }

理解Context

  • Context 提供了一种在组件之间共享数据的方式,不必显示地通过组件树逐层传递 props
  • 可以把 Context 当作特定组件树内共享的 store,用于做数据传递

JavaScript作用域链规则

  • JavaScript 代码块在执行期间,会创建一个相应的作用域链
  • 该作用域链记录着运行时的 JavaScript 代码块,在执行期间所能访问的活动对象,包括变量和函数
  • JavaScript 程序通过作用域链可以访问到代码块内部和外部的变量和函数

Context 类比作用域链

  • 以 JavaScript 作用域链作为类比,Context 对象好比一个提供给后代组件访问的作用域 ,其属性可以看成作用域上的活动对象
  • 由于 createContext 创建出一个 context 上下文对象,使用其提供的 Provider 对所有后代组件进行包裹,并提供数据
  • 所以后代组件可以类似通过作用域链一样,使用 context 上下文访问组件自身外部的变量和函数

React 并不推荐优先考虑使用 Context

  • Context 应用场景: 很多不同层级的组件需要访问同样的数据
  • Context 带来的影响: 使得组件的复用性变差
  • 如果只是想避免层层传递属性,组件组合有时候是比 Context 更好的解决方案
相关推荐
耶啵奶膘6 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test5 小时前
js下载excel示例demo
前端·javascript·excel
Yaml45 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事5 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro