• 【React组件】github搜索案例之 兄弟组件通信---PubSubJS / fetch


    💥回顾上篇文章的github案例,通过父子组件传值进而实现了兄弟组件之间的通信。
    本篇文章教你 如何直接在兄弟组件之间进行通信

    在这里插入图片描述

    🏧消息订阅-发布机制—axios发送请求

    该方法可以实现任意组件的通信
    假设A组件想给B组件发送消息(数据),A组件发布消息,B组件(接收数据)就要订阅该消息,这样B组件就能收到A组件传过来的数据了。

    在上篇文章的github案例中,search组件发送请求返回的数据用来发布消息List组件用来订阅该消息,这样通过消息订阅-发布机制就实现了兄弟组件通信。


    🔨工具库

    PubSubJS

    下载PubSubJS:

    npm install pubsub-js --save
    
    • 1

    引入PubSubJS:

    import PubSub from 'pubsub-js'   
    
    • 1

    📢订阅消息

    PubSub.subscribe('msg', function(msg,data){ }); 
    
    • 1

    第一个参数msg是订阅的事件名称
    第二个参数function(msg,data){ }是别人发布了消息之后执行的函数 (data是别人发布消息传递过来的数据)

    🔊发布消息

    PubSub.publish('msg', data)
    
    • 1

    📝实现步骤

    1️⃣分别在Search组件和List组件中引入PubSubJS

    import PubSub from 'pubsub-js' 
    
    • 1

    2️⃣List组件订阅消息:当组件挂载完毕时订阅消息

    componentDidMount() {
      this.token = PubSub.subscribe('atguigu', (_, stateObj) => {
        this.setState(stateObj)
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3️⃣Search组件使用axios发送请求获取数据并发布消息:

    在这里插入图片描述

    4️⃣List组件将要卸载时,取消订阅:

    componentWillUnmount() {
      PubSub.unsubscribe(this.token)
    }
    
    • 1
    • 2
    • 3

    📌扩展—fetch发送请求

    利用fetch()方法发送请求,原先使用axios发送请求,利用XMLHttpRequest封装的一个库,将一个复杂的过程,拆分成多个小操作,例如xhr.open()/xhr.send()

    而fetch是基于 Promise的一个内置的原生函数,不需要下载,不再使用XmlHttpRequest对象提交ajax请求,使用的是关注分离的设计思想,老版本浏览器可能不支持。

    Search组件

    search = async () => {
      // 获取用户的输入(解构赋值的连续写法+重命名)
      const { keyWordElement: { value: keyWord } } = this
      // 发送请求前通知List更新状态
      PubSub.publish('atguigu', { isFirst: false, isLoading: true })
      // 发送网络请求----使用fetch发送
      try {
        //是否能够联系服务器成功
        const response = await fetch(`/api1/search/users?q=${keyWord}`)
        //拿到数据
        const data = await response.json()
        PubSub.publish('atguigu', { isLoading: false, users: data.items })
      } catch (error) {
        PubSub.publish('atguigu', { isLoading: false, err: error.message })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用PubSub + fetch实现整体代码:

    index.js:

    // 引入react核心库
    import React from 'react'
    // 引入ReactDOM
    import ReactDOM from 'react-dom/client'
    // 引入App
    import App from './App'
    
    // 创建虚拟DOM
    const root = ReactDOM.createRoot(document.getElementById('root'))
    // 渲染虚拟DOM到页面
    root.render(
      <React.StrictMode>
      {/* 检查App组件以及子组件里的一些代码是否有不合理的地方 */}
      <App />
      </React.StrictMode>
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    父组件App.jsx:

    import React, { Component } from 'react';
    import Search from './components/Search'
    import List from './components/List';
    
    export default class App extends Component {
      render() {
        return (
          <div className="container">
            <Search />
            <List />
          </div>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    子组件Search_index.jsx:

    import React, { Component } from 'react'
    import PubSub from 'pubsub-js'
    // import axios from 'axios'
    
    export default class Search extends Component {
      search = async () => {
        // 获取用户的输入(解构赋值的连续写法+重命名)
        const { keyWordElement: { value: keyWord } } = this
        // 发送请求前通知List更新状态
        PubSub.publish('atguigu', { isFirst: false, isLoading: true })
        // 发送网络请求----使用fetch发送(未优化)
        // fetch(`/api1/search/users2?q=${keyWord}`).then(
        //   response => {
        //     console.log('成功了')
        //     return response.json()
        //   },
        //   error => {
        //     console.log('失败了', error)
        //     return new Promise(() => { })
        //   }
        // ).then(
        //   response => { console.log('获取数据成功了', response) },
        //   error => { console.log('获取数据失败了', error) }
        // )
        
        // 发送网络请求----使用fetch发送(优化)
        // fetch(`/api1/search/users2?q=${keyWord}`).then(
        //   response => {
        //     console.log('成功了')
        //     return response.json()
        //   }
        // ).then(
        //   response => { console.log('获取数据成功了', response) }
        // ).catch(
        //   (error) => {
        //     console.log(error)
        //   }
        // )
    
        // 发送网络请求----使用fetch发送(最优化)
        try {
          const response = await fetch(`/api1/search/users?q=${keyWord}`)
          const data = await response.json()
          PubSub.publish('atguigu', { isLoading: false, users: data.items })
        } catch (error) {
          PubSub.publish('atguigu', { isLoading: false, err: error.message })
        }
      }
    
      render() {
        return (
          <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索github用户</h3>
            <div>
              <input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索" />&nbsp;
              <button onClick={this.search}>搜索</button>
            </div>
          </section>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    子组件List_index.jsx:

    import React, { Component } from 'react'
    import PubSub from "pubsub-js";
    import './index.css'
    
    export default class List extends Component {
    
      state = { //初始化状态
        users: [], //users初始值为数组
        isFirst: true, //是否为第一次打开页面
        isLoading: false, //标识是否处于加载中
        err: '' //存储请求相关的错误信息
      }
    
      // 组件挂载完毕之后订阅消息
      componentDidMount() {
        this.token = PubSub.subscribe('atguigu', (_, stateObj) => {
          this.setState(stateObj)
        })
      }
    
      // 组件将要卸载时,取消订阅
      componentWillUnmount() {
        PubSub.unsubscribe(this.token)
      }
    
    
      render() {
        const { users, isFirst, isLoading, err } = this.state
        return (
          <div className="row">
            {
              isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
                isLoading ? <h2>Loading.....</h2> :
                  err ? <h2 style={{ color: 'red' }}>{err}</h2> :
                    users.map((userObj) => {
                      return (
                        <div key={userObj.id} className="card">
                          <a rel="noreferrer" href={userObj.html_url} target="_blank">
                            <img alt="avatar" src={userObj.avatar_url} style={{ width: '100px' }} />
                          </a>
                          <p className="card-text">{userObj.login}</p>
                        </div>
                      )
                    })
            }
          </div>
        )
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    setupProxy.js:

    const { createProxyMiddleware } = require('http-proxy-middleware')
    
    module.exports = function (app) {
      app.use(
        createProxyMiddleware('/api1', {
          target: 'http://localhost:5000',
          changeOrigin: true,
          pathRewrite: { '^/api1': '' }
        })
      )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    📦总结

    1️⃣设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。

    2️⃣ES6小知识点:解构赋值+重名名

    let obj = {a:{b:1}}
        const {a} =obj;  //传统解构赋值
        const {a:{b}} = obj;  //连续解构赋值
        const {a:{b:value}} = obj;  //连续解构赋值+重命名
    
    • 1
    • 2
    • 3
    • 4

    3️⃣消息订阅与发布机制

    1. 先订阅,再发布(理解:有一种隔空对话的感觉)
    2. 适用于任意组件间通信
    3. 要在组件的componentWillUnmount中取消订阅

    4️⃣fetch发送请求(关注分离的设计思想)

    try {
          const response = await fetch(`/api1/search/user2?q=${keyWord}`)
          const data = await response.json()
          console.log(data)
        } catch(error) {
          console.log('请求出错',error)
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    今天的分享就到这里啦 ✨
    如果对你有帮助的话,还请👉🏻关注💖点赞🤞收藏⭐评论🔥哦
    不定时回访哟🌹

  • 相关阅读:
    趣学python编程 (一、计算机基础知识科普)
    算法: 求数幂n次方50. Pow(x, n)
    基于VUE + Echarts 实现可视化数据大屏农村信息可视化
    现货伦敦金分析中如何应用支撑与压力位
    kafka 伊始,架构模型,角色功能梳理
    Java的CAS和synchronized原理解析【干货】
    本地PHP搭建简单Imagewheel私人云图床,在外远程访问——“cpolar内网穿透”
    全网最简约的Vscode配置Anaconda环境(百分百成功)
    构建知识库:一文解决跨平台科研文献及笔记同步问题
    92、Redis ------- 使用 Lettuce 操作 Redis 的方法和步骤----(文字讲解无代码)
  • 原文地址:https://blog.csdn.net/xuxuii/article/details/125602614