父传子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: '轮播图'
   }
 }
        propTypes使用方式文档 :zh-hans.legacy.reactjs.org/docs/typech...
子传父触发函数
- 同样通过 
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提供了称为Context的API
Context的作用和目的
- 作用: 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 
props 
- 目的: 为了共享那些对于组件树而言是"全局"的数据
 
React.createContext
createContext用于创建一个需要共享的Context对象,接收一个参数defaultValuedefaultValue是组件在顶层查找过程中未找到对应的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可嵌套使用,里层的会覆盖外层相同的数据 - 当 
Provider的value值发生变化时,其内部所有后代组件都会重新渲染 - 若某个后代组件订阅了 
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更好的解决方案