第四章 React ajax
三、案例 -- github 用户搜索
2. 代码实现
2.3 axios 发送请求
Search
javascript
复制代码
/* src/components/Search/index.jsx */
import React, { Component } from "react";
import axios from 'axios'
export default class Search extends Component {
search = () => {
//获取用户的输入(连续解构赋值+重命名)
const { keyWordElement:{value:keyword} } = this
console.log(keyword);
//发送网络请求
axios.get(`https://api.github.com/search/users?q=${keyword}`).then(
response => {console.log('c', response.data);},
error => {console.log('d', error);}
)
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">Search Github Users</h3>
<div>
<input ref={c => this.keyWordElement = c} type="text" placeholder="enter the name you search" />
<button onClick={this.search}>Search</button>
</div>
</section>
);
}
}
2.4 展示数据
2.4.1 App
javascript
复制代码
/* src/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:[]} //初始化状态,users初始值为数组
saveUsers = (users)=>{
this.setState({users})
}
render() {
const {users} = this.state
return (
<div className="container">
<Search saveUsers={this.saveUsers}/>
<List users={users}/>
</div>
);
}
}
2.4.2 Search
javascript
复制代码
/* src/components/Search/index.jsx */
import React, { Component } from "react";
import axios from 'axios'
export default class Search extends Component {
search = () => {
//获取用户的输入(连续解构赋值+重命名)
const { keyWordElement:{value:keyword} } = this
//发送网络请求
axios.get(`https://api.github.com/search/users?q=${keyword}`).then(
response => {
this.props.saveUsers(response.data.items)
},
error => {console.log('d', error);}
)
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">Search Github Users</h3>
<div>
<input ref={c => this.keyWordElement = c} type="text" placeholder="enter the name you search" />
<button onClick={this.search}>Search</button>
</div>
</section>
);
}
}
2.4.3 List
javascript
复制代码
/* src/components/List/index.jsx */
import React, { Component } from "react";
import "./index.css";
export default class List extends Component {
render() {
return (
<div className="row">
{this.props.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>
);
}
}
2.5 完成案例
2.5.1 App
javascript
复制代码
/* src/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: [], //users初始值为数组
isFirst: true, //是否为第一次打开页面
isLoading: false, //标识是否处于加载中
err: "", //存储请求相关的错误信息
};
//更新App的state
updateAppState = (stateObj) => {
this.setState(stateObj);
};
render() {
const { users } = this.state;
return (
<div className="container">
<Search updateAppState={this.updateAppState} />
<List {...this.state} />
</div>
);
}
}
2.5.2 Search
javascript
复制代码
/* src/components/Search/index.jsx */
import React, { Component } from "react";
import axios from "axios";
export default class Search extends Component {
search = () => {
//获取用户的输入(连续解构赋值+重命名)
const {
keyWordElement: { value: keyword },
} = this;
//发送请求前通知App更新状态
this.props.updateAppState({ isFirst: false, isLoading: true });
//发送网络请求
axios.get(`https://api.github.com/search/users?q=${keyword}`).then(
(response) => {
//请求成功后通知App更新状态
this.props.updateAppState({
isLoading: false,
users: response.data.items,
});
},
(error) => {
//请求失败后通知App更新状态
this.props.updateAppState({ isLoading: false, err: error.message });
}
);
};
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">Search Github Users</h3>
<div>
<input
ref={(c) => (this.keyWordElement = c)}
type="text"
placeholder="enter the name you search"
/>
<button onClick={this.search}>Search</button>
</div>
</section>
);
}
}
2.5.3 List
javascript
复制代码
/* src/components/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>Welcome, enter a keyword and then click search!</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>
);
}
}
四、消息订阅-发布机制
1. 工具库
2. 下载
- npm install pubsub-js --save
3. 使用
- import PubSub from 'pubsub-js' //引入
- PubSub.subscribe('delete', function(data){ }); //订阅
- PubSub.publish('delete', data) //发布消息
4. github 用户搜索代码重构
4.1 App
javascript
复制代码
/* src/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>
);
}
}
4.2 Search
javascript
复制代码
/* src/components/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;
//发送请求前通知List更新状态
PubSub.publish("alex", { isFirst: false, isLoading: true });
//发送网络请求
axios.get(`https://api.github.com/search/users?q=${keyword}`).then(
(response) => {
//请求成功后通知List更新状态
PubSub.publish("alex", {
isLoading: false,
users: response.data.items,
});
},
(error) => {
//请求失败后通知List更新状态
PubSub.publish("alex", { isLoading: false, err: error.message });
}
);
};
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">Search Github Users</h3>
<div>
<input
ref={(c) => (this.keyWordElement = c)}
type="text"
placeholder="enter the name you search"
/>
<button onClick={this.search}>Search</button>
</div>
</section>
);
}
}
4.3 List
javascript
复制代码
/* src/components/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("alex", (_, stateObj) => {
this.setState(stateObj);
});
}
componentWillUnmount() {
PubSub.unsubscribe(this.token);
}
render() {
const { users, isFirst, isLoading, err } = this.state;
return (
<div className="row">
{isFirst ? (
<h2>Welcome, enter a keyword and then click search!</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>
);
}
}
五、扩展:Fetch
1. 文档
2. 特点
- fetch: 原生函数,不再使用 XmlHttpRequest 对象提交 ajax 请求
- 老版本浏览器可能不支持
3. 相关 API
3.1 GET 请求
javascript
复制代码
fetch(url).then(function(response) {
return response.json()
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
});
3.2 POST 请求
javascript
复制代码
fetch(url, {
method: "POST",
body: JSON.stringify(data),
}).then(function(data) {
console.log(data)
}).catch(function(e) {
console.log(e)
})
4. github 用户搜索代码 - fetch
Search
javascript
复制代码
/* src/components/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
//发送请求前通知List更新状态
PubSub.publish('alex',{isFirst:false,isLoading:true})
//#region 发送网络请求---使用axios发送
/* axios.get(`https://api.github.com/search/users?q=${keyWord}`).then(
response => {
//请求成功后通知List更新状态
PubSub.publish('alex',{isLoading:false,users:response.data.items})
},
error => {
//请求失败后通知App更新状态
PubSub.publish('alex',{isLoading:false,err:error.message})
}
) */
//#endregion
//发送网络请求---使用fetch发送(未优化)
/* fetch(`https://api.github.com/search/users?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发送(优化)
try {
const response= await fetch(`https://api.github.com/search/users?q=${keyWord}`)
const data = await response.json()
PubSub.publish('alex',{isLoading:false,users:data.items})
} catch (error) {
PubSub.publish('alex',{isLoading:false,err:error.message})
}
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">Search Github Users</h3>
<div>
<input
ref={(c) => (this.keyWordElement = c)}
type="text"
placeholder="enter the name you search"
/>
<button onClick={this.search}>Search</button>
</div>
</section>
);
}
}
六、总结
bash
复制代码
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);
}