10. React组件间的通信

发布于:2025-08-13 ⋅ 阅读:(20) ⋅ 点赞:(0)

3. React组件间的通信

  • 3.1. 认识组件嵌套
    • 1. 组件之间存在嵌套关系:
      • 1.1. 在之前的案例中,只是创建了一个组件App;
      • 1.2. 如果我们一个应用程序将所有的逻辑都放在一个组件中, 那么这个组件就会变得非常的臃肿和难以维护;
      • 1.3. 所以组件化的核心思想应该是对组件进行拆分,拆分成一个个小的组件;
      • 1.4. 在将这些组件组合嵌套在一起,最终形成应用程序;
    • 2. 上面的嵌套逻辑如下,它们存在如下关系:
      • 2.1. App组件是Header、Main、Footer组件的父组件;
      • 2.2. Main组件是Banner、ProductList组件的父组件
      • 2.3. 如下图
        在这里插入图片描述
    • 3. 提高开发效率的vscode插件
      1. 原因:每次开发时都需要引入组件,都需要引入相关的依赖,很麻烦。
      1. 使用插件:ES7+ React/Redux/React-Native snippets, 这个插件会包含很多的代码片段还挺好用的,所以使用了这个插件的代码片段,
  • 3.2. 认识组件嵌套
    • 3.2.1 在开发过程中,我们会经常遇到需要组件之间相互通信:
        1. App可能使用了多个Header, 每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示
        1. 又比如在Main中一次性请求了Banner数据和ProductList数据,那么就需要传递给他们来进行展示;
        1. 也可能是子组件中发生了事件,需要又父组件来完成某些操作,那就需要子组件向父组件传递事件;
    • 3.2.2 总之,在一个React项目中,组件之间通信是非常重要的环节;
    • 3.2.3 父组件在展示子组件,可能会传递一些数据给子组件;
        1. 父组件通过属性=值的形式来传递给子组件数据;
        1. 父组件通过props参数获取父组件传递过来的数据
        1. 示例代码如下:
        • 父组件:
          import React, { Component } from 'react'
          import MainBanner from './MainBanner'
          import MainProductList from './MainProductList'
        
          export class Main extends Component {
            constructor () {
              super()
        
              this.state = {
                title: '商品轮播图',
                banners: ['新歌单','新MV', '新歌曲'],
                productList: ['推荐商品', '热门商品', '流行商品']
              }
            }
            render() {
              const { title, banners, productList} = this.state
              return (
                <div className='main'>
                  <div>main</div>
                  <MainBanner banners={banners} title={title} />
                  <MainProductList productList={productList} />
                </div>
              )
            }
          }
        
          export default Main
        
        • 子组件
        import React, { Component } from 'react'
        
        export class MainBanner extends Component {
          constructor(props) {
            // 把props传递给super,内部会自动保存执行这个操作this.props = props
            // 如果不写constructor,默认就会执行super(props);自动保存this.props = props
            super(props)
            this.state = {
              name: 'MainBanner'
            }
          }
          render() {
            const { title, banners} = this.props
            console.log('props==', this.props)
        
            return (
              <div>
                <h2>{title}</h2>
                <ul>
                  {
                    banners.map(item => {
                      return (
                        <li key={item}>{item}</li>
                      )
                    })
                  }
                </ul>
              </div>
            )
          }
        }
        
        export default MainBanner
        
  • 3.3. 参数propsTypes
    • 3.1. 对于传递给子组件的数据,有时候我们可能希望进行验证,特别是大型项目来说:
        1. 当然,如果你项目中默认继承了Flow或者TypeScript,那么直接就可以进行类型验证
        1. 但是,即使我们没有使用Flow或者TypeScript,也可以通过props-types库来进行参数验证
    • 3.2. 从React v15.5开始,React.PropTypes已移入另一个包中:prop-types库;
    • 3.3. 更多的验证方式,可以参考官网:官网地址
      • 1.比如验证数组,并且数组中包含哪些元素;

      • 2.比如验证对象,并且对象中包含哪些key以及value是什么类型

      • 3.比如某个原生是必须的,使用requiredFunc: PropTypes.func.isRequired;

        • 设置必选和指定类型
          import React, { Component } from 'react'
          import PropTypes from 'prop-types';
        
          export class MainBanner extends Component {
            constructor(props) {
              // 把props传递给super,内部会自动保存执行这个操作this.props = props
              // 如果不写constructor,默认就会执行super(props);自动保存this.props = props
              super(props)
              this.state = {
                name: 'MainBanner'
              }
            }
            render() {
              const { title, banners} = this.props
        
              return (
                <div>
                  <h2>{title}</h2>
                  <ul>
                    {
                      banners.map((item) => {
                        return (
                          <li key={item.acm}>
                            {item.title}
                          </li>
                        )
                      })
                    }
                  </ul>
                </div>
              )
            }
          }
        
          // MainBanner.propTypes = {
          //   banners: PropTypes.array.isRequired,
          //   title: PropTypes.string.isRequired,
          // };
        
          MainBanner.propTypes = {
            banners: PropTypes.array.isRequired,
            title: PropTypes.string.isRequired
          };
        
          export default MainBanner
        
        • 不传值或者传值类型为其他类型,会报错
          import React, { Component } from 'react'
          // import MainBanner from './MainBanner'
          import MyComponent from './MainBanner'
          import MainProductList from './MainProductList'
          import axios from 'axios'
        
          export class Main extends Component {
            constructor () {
              super()
        
              this.state = {
                title: '商品轮播图',
                banners: [],
                productList: []
              }
            }
            componentDidMount () {
              axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
                console.log(res);
                const data = res.data.data
                const banners = data.banner.list
                const recommend = data.recommend.list
                this.setState({
                  banners,
                  productList: recommend
                })
                
              })
            }
            
            render() {
              const {  title, banners, productList} = this.state
              // 
              return (
                <div className='main'>
                  <div>main</div>
                  <MyComponent banners={banners} title={title}/>
                  <MyComponent />
                  <MainProductList productList={productList} />
                </div>
              )
            }
          }
        
          export default Main
        
        • 报错图如下:
          在这里插入图片描述

        在这里插入图片描述

        • 正确传参:
          import React, { Component } from 'react'
        // import MainBanner from './MainBanner'
        import MyComponent from './MainBanner'
        import MainProductList from './MainProductList'
        import axios from 'axios'
        
        export class Main extends Component {
          constructor () {
            super()
        
            this.state = {
              title: '商品轮播图',
              banners: [],
              productList: []
            }
          }
          componentDidMount () {
            axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
              console.log(res);
              const data = res.data.data
              const banners = data.banner.list
              const recommend = data.recommend.list
              this.setState({
                banners,
                productList: recommend
              })
              
            })
          }
          
          render() {
            const {  title, banners, productList} = this.state
            // 
            return (
              <div className='main'>
                <div>main</div>
                <MyComponent banners={banners} title={title}/>
                <MyComponent banners={banners} title={title}/>
                <MainProductList productList={productList} />
              </div>
            )
          }
        }
        
        export default Main
        
    • 3.4. 如果没有传递的话,希望有默认值;
        1. 使用defaultProps就可以了
  • 3.4. 子组件传递父组件
    • 3.4.1. 某些情况,我们也需要子组件向父组件传递消息:
        1. 在Vue中是通过自定义事件来完成的;
        1. 在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数在子组件中调用这个函数即可
    • 3.4.2. 这里完成一个案例
        1. 将计数器案例进行拆解
        1. 将按钮封装到子组件中:CounterButton;
        1. CounterButton发生点击事件,将内容传递给父组件,修改counter的值;
    • 3.4.3. 示例代码如下:
      • 父组件:App.jsx
        import React, { Component } from 'react'
        import AddCount from './AddCounter'
        import SubCount from './SubCounterr'
      
        export class App extends Component {
          constructor() {
            super()
            this.state = {
              counter: 0
            }
          }
          changeCounter (counter) {
            this.setState({
              counter: this.state.counter + counter
            })
          }
          render() {
            const { counter } = this.state
            return (
              <div>
                <h2>当前计数:{counter}</h2>
                <AddCount addClick={counter => this.changeCounter(counter) } />
                <SubCount subClick={counter => this.changeCounter(counter) } />
              </div>
            )
          }
        }
      
        export default App
      
      • 子组件:AddCounter.jsx
        import React, { Component } from 'react'
      
        export class AddCount 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>
            )
          }
        }
      
        export default AddCount
      
      • 子组件:SubCounter.j
        import React, { Component } from 'react'
      
        export class subCount extends Component {
          subCount (count) {
            this.props.subClick(count)
          }
          render() {
            return (
              <div>
                <button onClick={e => this.subCount(-1)}>-1</button>
                <button onClick={e => this.subCount(-5)}>-5</button>
                <button onClick={e => this.subCount(-10)}>-10</button>
              </div>
            )
          }
        }
      
        export default subCount
      
  • 3.5. 子组件传父组件案例练习
    • 1.如下图
      在这里插入图片描述

      1. 父组件App代码:
        import React, { Component } from 'react'
        import TabControl from './TabControl'
      
        export class App extends Component {
          constructor() {
            super()
      
            this.state = {
              titles: ['流行', '新款', '热门'],
              tabIndex: 0
            }
          }
          tabClick (index) {
            this.setState({
              tabIndex: index
            })
          }
          render() {
            const { titles,tabIndex } = this.state
            return (
              <div className='app'>
                <TabControl titles={ titles } tabClick={i => this.tabClick(i)}/>
                  <h2>{titles[tabIndex]}</h2>
              </div>
            )
          }
        }
      
        export default App
      ``
      1. 子组件TabControl代码
        import React, { Component } from 'react'
        import './style.css'
      
      
        export class TabControl extends Component {
          constructor() {
            super()
            this.state = {
              currentIndex: 0
            }
          }
      
          changeTab (index) {
            this.props.tabClick(index)
            this.setState({
              currentIndex: index
            })
          } 
      
          render() {
            const { titles } =  this.props
            const { currentIndex } = this.state
            return (
              <div>
                <div className="tab-control">
                    {
                      titles.map((item, index) => {
                        return (
                        <div 
                          className={`item ${currentIndex === index ? 'active' : ''}`} 
                          key={index} 
                          onClick={(e) => this.changeTab(index)}>
                          <span className='text'>{item}</span>
                        </div>
                        )
                      })
                    }
                </div>
              </div>
            )
          }
        }
      
        export default TabControl
      

网站公告

今日签到

点亮在社区的每一天
去签到