💥回顾上篇文章的github案例,通过父子组件传值进而实现了兄弟组件之间的通信。
本篇文章教你如何直接在兄弟组件之间进行通信
。
该方法可以实现任意组件的通信
假设A组件想给B组件发送消息(数据),A组件发布消息,B组件(接收数据)就要订阅该消息,这样B组件就能收到A组件传过来的数据了。
在上篇文章的github案例中,
search组件
发送请求返回的数据用来发布消息
,List组件
用来订阅该消息
,这样通过消息订阅-发布机制就实现了兄弟组件通信。
下载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()方法发送请求,原先使用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="输入关键词点击搜索" />
<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️⃣消息订阅与发布机制
先订阅,再发布
(理解:有一种隔空对话的感觉)取消订阅
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)
}
今天的分享就到这里啦 ✨
如果对你有帮助的话,还请👉🏻关注💖点赞🤞收藏⭐评论🔥哦
不定时回访哟🌹