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

在这里插入图片描述

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

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

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


🔨工具库

PubSubJS

下载PubSubJS:

npm install pubsub-js --save

引入PubSubJS:

import PubSub from 'pubsub-js'   

📢订阅消息

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

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

🔊发布消息

PubSub.publish('msg', data)

📝实现步骤

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

import PubSub from 'pubsub-js' 

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

componentDidMount() {
  this.token = PubSub.subscribe('atguigu', (_, stateObj) => {
    this.setState(stateObj)
  })
}

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

在这里插入图片描述

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

componentWillUnmount() {
  PubSub.unsubscribe(this.token)
}

📌扩展—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 })
  }
}

使用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>
)

父组件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>
    );
  }
}

子组件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>
    )
  }
}

子组件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>
    )
  }
}

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️⃣ES6小知识点:解构赋值+重名名

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

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)
    }

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

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐