案例最终效果如下:
可将页面拆成三个组件
目录如下
1.思路
- 初始化状态
- 用户点击search按钮后,根据input中的value值获取内容并更新到状态中
- 发送网络请求,将value拼接得到response并更新状态
- 请求的数据进行展示
- 完善用户体验,添加一些状态,第一次输入时,正在加载时,响应错误时
2.主要逻辑代码
脚手架配置代理,解决跨域问题,setupProxy.js
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
proxy('/api1',{ // 遇见/api1前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', // 请求转发给谁
changeOrigin: true, // 控制服务器收到的请求头中Host的值
pathRewrite: {'^/api1':''} // 重写请求路径(必须)
})
)
}
App.js
// 创建“外壳”组件App
import React,{Component} from 'react'
import Search from "./components/Search/index";
import List from "./components/List/index";
import "./App.css"
class App extends Component{
// 初始化状态
state = {
users: [], //users初始值为数组
isFirst: true, //是否为第一次打开页面
isLoading: false, //是否处于加载中
err: '', //存储请求的相关信息
}
// 更新App的state
updateAppState = (stateObj) => {
// 因为本身就是一个对象,所以不用写{}
this.setState(stateObj)
}
render() {
return (
<div className="total">
<Search updateAppState={this.updateAppState}/>
<List {...this.state}/>
</div>
)
}
}
export default App
Search.js
import React,{Component} from "react"
import axios from "axios";
import "./index.css"
export default class Search extends Component {
// 点击搜索按钮搜索
search = () => {
// 拿到输入框的数据
const {inputElement} = this
console.log(inputElement.value)
// 发送请求前通知App更新状态
this.props.updateAppState({isFirst:false,isLoading:true})
// 发送网络请求
axios.get(`http://localhost:3000/api1//search/users?q=${inputElement.value}`).then(
response => {
console.log(response.data.items)
// 请求成功后通知App更新状态
this.props.updateAppState({isLoading: false, users: response.data.items})
},
error => {
// 请求失败后通知App更新状态
this.props.updateAppState({isLoading:false,err:error.message})
}
)
}
render() {
return (
<div className="search">
<div className="container">
<h3>Search Github Users</h3>
<input type="text" ref={c => this.inputElement = c} placeholder="enter the name you search"/>
<button onClick={this.search}>Search</button>
</div>
</div>
)
}
}
List.js
import React,{Component} from "react"
import Item from "../Item/index";
import "./index.css"
export default class List extends Component {
render() {
const {users,isFirst,isLoading,err} = this.props
return (
<ul className="totalPhoto">
{
// 必须写表达式,所以不能写if语句
isFirst? <h2>欢迎使用,输入关键字,随后点击搜索</h2>: isLoading? <h2>Loading...</h2>:
err? <h2>err</h2>:
users.map((user)=> {
return <Item key={user.id} user={user} {...user}/>
})
}
</ul>
)
}
}
Item.js
import React,{Component} from "react"
import "./index.css"
export default class Item extends Component {
render() {
const {user} = this.props
return (
<div className="photo">
<section>
<a href={user.html_url} target="_blank">
<img src={user.avatar_url} alt=""/>
</a>
<p className="text">{user.login}</p>
</section>
</div>
)
}
}
3.知识点和注意点
- 发送网络请求的那一套模板
- 简单了解并解决跨域问题
- jsx的{}中要写表达式,就像可以写三元表达式但不能写if语句,详情见以前的博客 jsx语法规则
- 对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
- 遍历时要有唯一key值
4.拓展:用fetch发送网络请求(关注分离)
// 用fetch发送请求 (未优化)
fetch(`http://localhost:3000/api1//search/users2?q=${inputElement.value}`).then(
response => {
console.log('联系服务器成功了');
return response.json()
},
error => {
console.log('联系服务器失败了',error);
return new Promise(()=>{})
}
).then(
response => {
console.log('获取数据成功了',response)
},
error => {
console.log('获取数据失败了',error)
}
).catch(
error => {
console.log(error)
}
)
// 用fetch发送网络请求(优化)
try {
const response = await fetch(`http://localhost:3000/api1//search/users?q=${inputElement.value}`)
const data = await response.json()
this.props.updateAppState({isLoading: false, users: data.items})
} catch (error) {
console.log('请求出错',error)
}