一、没有用消息订阅与发布机制的案例
如果没有使用消息订阅与发布机制,兄弟组件想互相通信智能通过他们的父组件来进行管理,看一个github搜索案例
项目结构 index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App/>,document.getElementById('root'))
App.jsx
import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
export default class App extends Component {
state = {
users:[],
isFirst:true,
isLoading:false,
err:'',
}
updateAppState = (stateObj)=>{
this.setState(stateObj)
}
render() {
return (
<div className="container">
<Search updateAppState={this.updateAppState}/>
<List {...this.state}/>
</div>
)
}
}
setupProxy.js
const proxy = require('http-proxy-middleware')
module.exports = function(app){
app.use(
proxy('/api1',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api1':''}
})
)
}
Search下的index.jsx
import React, { Component } from 'react'
import axios from 'axios'
export default class Search extends Component {
search = ()=>{
const {keyWordElement:{value:keyWord}} = this
this.props.updateAppState({isFirst:false,isLoading:true})
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
this.props.updateAppState({isLoading:false,users:response.data.items})
},
error => {
this.props.updateAppState({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 './index.css'
export default class List extends Component {
render() {
const {users,isFirst,isLoading,err} = this.props
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="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
)
}
}
List下的index.css
.album {
min-height: 50rem;
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
二、使用消息订阅与发布机制的案例
同样的案例,如果使用了消息订阅与发布机制,那么兄弟组件之间的通信就不需要借助父组件了,通过消息订阅与发布就可以直接进行通信
项目结构 index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App/>,document.getElementById('root'))
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>
)
}
}
setupProxy.js
const proxy = require('http-proxy-middleware')
module.exports = function(app){
app.use(
proxy('/api1',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api1':''}
})
)
}
Search下的index.jsx
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import axios from 'axios'
export default class Search extends Component {
search = ()=>{
const {keyWordElement:{value:keyWord}} = this
PubSub.publish('atguigu',{isFirst:false,isLoading:true})
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
},
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:[],
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="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
)
}
}
List下的index.css
.album {
min-height: 50rem;
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text {
font-size: 85%;
}
三、fetch发送请求
上面的示例只需这样改动 Search里的index.jsx改成这样
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
export default class Search extends Component {
search = async()=>{
const {keyWordElement:{value:keyWord}} = this
PubSub.publish('atguigu',{isFirst:false,isLoading:true})
try {
const response= await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data);
PubSub.publish('atguigu',{isLoading:false,users:data.items})
} catch (error) {
console.log('请求出错',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>
)
}
}
四、总结
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/users2?q=${keyWord}`)
const data = await response.json()
console.log(data);
} catch (error) {
console.log('请求出错',error);
}
|