• 【React】github搜索案例实现兄弟组件通信(axios、PubSub、fetch)


    前言

    😎😎欢迎来到我的博客😎😎
    📔博主是一名大学在读本科生,主要学习方向是前端😊。
    🍭目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏🤩
    🛠目前正在学习的是🔥 R e a c t 框 架 React框架 React🔥,中间夹杂了一些基础知识的回顾⌨️
    🌈博客主页👉codeMak1r.的博客
    ➕关注👍点赞📂收藏


    本文被专栏【React–从基础到实战】收录
    前文回顾:setupProxy配置前端跨域

    🕹坚持创作✏️,一起学习📖,码出未来👨🏻‍💻!
    在这里插入图片描述

    github搜索案例

    在这里插入图片描述
    在本案例中,使用的是create-react-app搭建的脚手架,外面最大的是总的App组件。
    在App组件中可以分为两个子组件——Search以及List。
    Search包含搜索功能,在Search组件中发送网络请求,返回的数据由List组件渲染到页面中。
    这就表示一定存在Search组件到List组件的通信。也就是兄弟组件之间的通信。

    在这里插入图片描述

    axios + PubSubJS实现——消息订阅与发布

    PubSubJS是一个star✨数达到4.2k的一个库。
    主要功能就是实现任意组件之间的通信,运用的是消息订阅与发布机制。

    消息订阅:

    PubSub.subscribe('Message', func());
    
    • 1

    消息发布:

    PubSub.publish('Message', data);
    
    • 1

    需要使用数据的组件A订阅一个消息(消息名为订阅时自定义的)
    发起请求的组件B接收到服务器返回的数据后将数据作为消息发布出去,发布时指定发布的消息名,该消息名若被组件A订阅了,则会自动将数据传递给A组件,从而实现组件之间的通信。

    下载PubSubJS

    npm install pubsub-js
    yarn add pubsub-js
    ----
    sudo npm install pubsub-js    // MAC
    
    • 1
    • 2
    • 3
    • 4

    导入PubSubJS

    import PubSub from 'pubsub-js'
    
    // or when using CommonJS
    const PubSub = require('pubsub-js');
    
    • 1
    • 2
    • 3
    • 4

    下载与导入axios

    npm install axios  // 下载
    import axios from 'axios'  //导入
    
    • 1
    • 2

    首先我们要先分清,是谁需要订阅消息?谁需要发布消息?

    我们可以用订阅报纸来理解,
    我想要读取报纸的消息,我就需要订阅报纸;

    也就是——List组件想要读取数据,将数据渲染到页面中,那么List组件需要做的就是订阅消息。
    而Search组件发送请求,请求成功后将返回的数据交给List组件,那么Search就应该将返回的数据当作发布的消息,只要Search组件发布消息,订阅了消息的List组件就能够接收到。

    List组件订阅消息

    // 初始化状态
    state = {
        users: [],
        isFirst: true,  // 是否为第一次打开页面
        isLoading: false,  // 是否在加载中
        error: ''  // 存储请求相关的错误信息
      }
    componentDidMount() {
       // 组件一挂载时就订阅消息
       this.token = PubSub.subscribe('userMessage', (_, stateObj) => {
         this.setState(stateObj);
       })
     }
    componentWillUnmount() {
       // 组件卸载前取消订阅
       PubSub.unsubscribe(this.token)
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Search组件发起请求,发布消息

    // 发送网络请求
    axios.get(`/api1/search/users?q=${keyWord}`)
      .then(value => {
        // 请求成功后通知List更新状态
        const { data: { items: users } } = value
        PubSub.publish('userMessage', { isLoading: false, users })
      }, reason => {
        // 请求失败后通知List更新状态
        PubSub.publish('userMessage', { isLoading: false, error: reason.message })
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    fetch + PubSubJS实现——消息订阅与发布

    发起网络请求,并不一定需要axios,甚至都不一定需要XMLHttpRequest。
    JS原生的fetch就很好用。
    fetch也可以用来发起网络请求,且fetch并不是基于XMLHttpRequest的。
    fetch是基于 Promise的,使用的是关注分离的设计思想。

    Search组件(fetch)

    try {
      const response = await fetch(`/api1/search/users?q=${keyWord}`)
      const data = await response.json()
      PubSub.publish('userMessage', { isLoading: false, users: data.items })
    } catch (error) {
      console.log('出错啦', error)
      PubSub.publish('userMessage', { isLoading: false, error: error.message })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    github搜索案例源码

    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.jsx (axios + PubSub)

    import React, { Component } from 'react'
    import axios from 'axios'
    import PubSub from 'pubsub-js';
    
    export default class Search extends Component {
      search = () => {
        // 获取用户的输入(连续解构赋值)拿到this.inputRef.value + (重命名)value=>keyWord
        const { inputRef: { value: keyWord } } = this
    
        // 发送请求前通知List更新状态
        PubSub.publish('userMessage', { isFirst: false, isLoading: true })
    
    
        // 发送网络请求
        axios.get(`/api1/search/users234?q=${keyWord}`)
          .then(value => {
            // 请求成功后通知List更新状态
            const { data: { items: users } } = value
            PubSub.publish('userMessage', { isLoading: false, users })
          }, reason => {
            // 请求失败后通知List更新状态 
            PubSub.publish('userMessage', { isLoading: false, error: reason.message })
          })
      }
    
      enterSearch = (event) => {
        if (event.key !== 'Enter') return
        this.search()
      }
    
      render() {
        return (
          <section className="jumbotron">
            <h3 className="jumbotron-heading">Search Github Users</h3>
            <div>
              <input onKeyUp={this.enterSearch} 
              ref={currentNode => this.inputRef = currentNode} 
              type="text" placeholder="enter the name you search" 
              style={{ width: '180px' }} />&nbsp;
              <button onClick={this.search}>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

    Search.jsx (fetch + PubSub)

    import React, { Component } from 'react'
    import PubSub from 'pubsub-js';
    
    export default class Search extends Component {
    
      search = async () => {
        // 获取用户的输入(连续解构赋值)拿到this.inputRef.value + (重命名)value=>keyWord
        const { inputRef: { value: keyWord } } = this
    
        // 发送请求前通知List更新状态
        PubSub.publish('userMessage', { isFirst: false, isLoading: true })
        try {
          const response = await fetch(`/api1/search/users234?q=${keyWord}`)
          const data = await response.json()
          PubSub.publish('userMessage', { isLoading: false, users: data.items })
        } catch (error) {
          console.log('出错啦', error)
          PubSub.publish('userMessage', { isLoading: false, error: error.message })
        }
      }
    
      enterSearch = (event) => {
        if (event.key !== 'Enter') return
        this.search()
      }
    
      render() {
        return (
          <section className="jumbotron">
            <h3 className="jumbotron-heading">Search Github Users</h3>
            <div>
              <input onKeyUp={this.enterSearch} 
              ref={currentNode => this.inputRef = currentNode} 
              type="text" placeholder="enter the name you search" 
              style={{ width: '180px' }} />&nbsp;
              <button onClick={this.search}>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

    List.jsx

    import React, { Component } from 'react'
    import PubSub from 'pubsub-js'
    import './index.css'
    
    export default class List extends Component {
    
      state = {
        users: [],
        isFirst: true,  // 是否为第一次打开页面
        isLoading: false,  // 是否在加载中
        error: ''  // 存储请求相关的错误信息
      }
    
      componentDidMount() {
        // 组件一挂载时就订阅消息
        this.token = PubSub.subscribe('userMessage', (_, stateObj) => {
          this.setState(stateObj);
        })
      }
    
      componentWillUnmount() {
        // 组件卸载前取消订阅
        PubSub.unsubscribe(this.token)
      }
    
    
      render() {
        const { users, isFirst, isLoading, error } = this.state
        return (
          <div className="row">
            {
              isFirst ? <h2>Enter The Name You Search...</h2> :
                isLoading ? <h2>Loading...</h2> :
                  error ? <h2 style={{ color: 'red' }}>{error}</h2> :
                    users.map((userObj) => {
                      return (
                        <div key={userObj.id} className="card">
                          <a rel="noreferrer" href={userObj.html_url} target="_blank">
                            <img alt='head_sculpture' 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

    总结

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

    2. ES6小知识点:解构赋值 + 重命名

      let obj = {a: { b:1 }}
      const {a} = obj; // 传统解构赋值
      const {a: {b}} = obj // 连续解构赋值
      const {a: {b: value}} // 连续解构赋值 + 重命名
      
      • 1
      • 2
      • 3
      • 4
    3. 消息订阅与发布机制

      先订阅再发布;(理解:有一种隔空对话的感觉)

      适用于任意组件间的通信;

      要在组件的componentWillUnmount中取消订阅;

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

      try {
        const response = await fetch(`/api1/search/users?q=${keyWord}`)
        const data = await response.json()
        PubSub.publish('userMessage', { isLoading: false, users: data.items })
      } catch (error) {
        console.log('出错啦', error)
        PubSub.publish('userMessage', { isLoading: false, error: error.message })
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    🎉🎉如果觉得博主的文章还不错的话
    ➕关注博主👍点赞文章📁收藏文章
    ✏️原创不易你的支持将会是我最大的动力💪
    🧸感谢观看

  • 相关阅读:
    cpp基础学习笔记02
    大型连锁超市---LIDI验厂再出新规?
    信息学奥赛一本通1000:入门测试题目
    C++的内存管理和模板
    论文解读(AGC)《Attributed Graph Clustering via Adaptive Graph Convolution》
    [ 常用工具篇 ] AntSword 蚁剑安装及使用详解
    Git安装
    【408数据结构】第一章 绪论
    MyCat2之MOD_HASH算法
    会声会影2023最新旗舰版下载功能介绍
  • 原文地址:https://blog.csdn.net/Svik_zy/article/details/125540172