一、兄弟组件传值-发布订阅
- 之前的Github搜索案例,Search组件和List组件没有直接进行数据的通信,而是借用了App组件来完成
- 这个案例学习兄弟组件通信-发布订阅模式
首先引入第三方库PubSubJS
在Search.jsx和List.jsx导入
- import pubSub from ‘pubsub-js’
App.jsx中初始化的数据最终是由List组件完成的,所以把state放到List.jsx中,同时更新组件状态的方法也不在需要,也不必再给组件传值
现在的APP.jsx是当做一个外壳:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React, { Component } from 'react'; import Search from './components/Search'; import List from './components/List'; export default class App extends Component { render() { return ( <div> <Search /> <List /> </div> ); } }
|
在List.jsx中,组件挂载完后,订阅Search发布的消息:
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
| 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() { pubSub.subscribe('Github',(_,stateObj) => { this.setState(stateObj) }) }
render() { const { users, isFirst, isLoading, err } = this.state; return ( <div className="row"> {isFirst ? <h2>欢迎您</h2> : isLoading ? <h2>搜索中</h2> : err ? <h2>{err.message}</h2> : users.map((userObj) => { return ( <div className="card" key={userObj.id}> <a rel="noreferrer" href={userObj.html_url} target="_blank"> <img alt="head" src={userObj.avater_url} style={{ width: '100px' }} /> </a> <p className="card-text">{userObj.login}</p> </div> ); }) } </div> ); } }
|
同时Search.jsx中要发布消息,并把状态对象传递给List组件来更新组件状态:
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
| import React, { Component } from 'react'; import axios from 'axios'; import pubSub from 'pubsub-js';
export default class Search extends Component { search = () => { const { keyWordElement: { value: keyWord }, } = this; //发起请求前loading pubSub.publish('Github', { isFirst: false, isLoading: true }); // this.props.updateStates({ isFirst:false, isLoading: true }); axios.get(`/api1/search/users?q=${keyWord}`).then( (res) => { //请求数据成功显示数据 pubSub.publish('Github', { isLoading: false, users: res.data.items }); // this.props.updateStates({ isLoading: false, users: res.data.items }); }, (err) => { //失败后获取失败信息 pubSub.publish('Github', { isLoading: false, err }); // this.props.updateStates({isLoading:false,err}); } ); }; render() { return ( <section className="jumbotron"> <h3 className="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="请输入github用户名字" ref={(c) => (this.keyWordElement = c)} /> <button onClick={this.search}>搜索</button> </div> </section> ); } }
|
这就实现了整个搜索案例
不再使用App代理的方式,改用发布订阅的方式来实现兄弟组建的传值
这种方式不仅仅适用于兄弟组件,任意两组件都可以
二、Fetch
不管是jQuery还是axios,底层都是基于xhr对象的封装。
axios采用promise的方式
Fetch也是采用promise方式处理异步,但不基于xhr对象
1 2 3 4 5 6 7 8
| fetch(`/api1/search/users2?q=${keyWord}`).then( (res) => { console.log('发送请求给服务器成功', res); }, (err) => { console.log('发送请求给服务器失败', err); } );
|
这样无论如何都会返回一些信息,但是拿不到我们想要的数据
因为这一步只是证明我们联系服务器成功了。
路径都正确时返回的信息:

路由错误时返回信息:

可以看到都返回信息,但是状态码等信息发生了变化
如何拿到想要的数据呢?
1 2 3
| (res) => { console.log('发送请求给服务器成功', res.json()); },
|

数据就有了,但是注意.json()返回的是一个promise对象,所以我们还得.then一下
1 2 3 4 5 6 7 8 9 10 11 12
| fetch(`/api1/search/users2?q=${keyWord}`).then( (res) => { console.log('发送请求给服务器成功',res); return res.json() }, (err) => { console.log('发送请求给服务器失败', err); } ).then(res => { console.log(res); }) };
|
这样就能拿到数据了:

但是此时如果我们把网络断开发送请求,发现返回的数据时undefined
失败了就没必要再去打印res了,所以可以在err中返回一个初始的Promise终止
1 2 3 4
| (err) => { return new Promise(() => {}) }
|
这样就不会继续.then()了
优化fetch:
上面的写法过于复杂
可以使用asynx/await改造
1 2 3 4 5 6 7 8 9 10 11
| try { const res = await fetch(`/api1/search/users?q=${keyWord}`); console.log(res); const data = await res.json(); console.log(data); pubSub.publish('Github', { isLoading: false, users: data.items }); } catch (err) { pubSub.publish('Github', { isLoading: false, err }); } //记得上面的函数加 async //search = async () => {
|
三、Github案例总结
设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
ES6小知识点:解构赋值+重命名
1 2 3 4
| let obj = {a:{b:1}} const {a} = obj; const {a:{b}} = obj; const {a:{b:value}} = obj;
|
消息订阅与发布机制
- 先订阅,再发布(理解:有一种隔空对话的感觉)
- 适用于任意组件间通信
- 要在组件的componentWillUnmount中取消订阅
fetch发送请求(关注分离的设计思想)
1 2 3 4 5 6 7
| try { const response= await fetch(/api1/search/users2?q=${keyWord}) const data = await response.json() console.log(data); } catch (error) { console.log('请求出错',error); }
|