# 08_GitHub 搜索案例
📢 大家好,我是Milo
📢 这篇文章是学习 React 中 GitHub 搜索案例的学习笔记
# 引言
本文主要介绍 React 学习中 Github 搜索案例,这个案例主要涉及到了 Axios 发送请求,数据渲染以及一些中间交替效果的实现
个人感觉在做完 TodoList 案例之后,这个案例会很轻松,只是多加了一个 Loading 效果的实现思路,以及一些小细节的完善,感觉练练手还是很不错的
# 一、实现静态组件
和之前的 TodoList 案例一样,我们需要先实现静态组件
# 1 拆分组件
在实现静态组件之前,我们还需要拆分组件,这个页面的组件,我们可以将它拆成以下两个组件,第一个组件是 Search
,第二个是 List
# 2 实现静态组件
接下来我们需要将提前写好的静态页面,对应拆分到组件当中
首先,我们在 src
目录下,新建一个 components
文件夹,用于存放我们的组件,然后在文件夹下,新建 List
、Search
组件文件夹,再创建其下的 index.jsx
,index.css
文件,用于创建对应组件及其样式文件
其次,在 public
下创建 css
文件夹,放入 bootstrap.css
文件,记得引入 css文件
Github搜索案例
├─ package.json
├─ public
│ ├─ css
│ │ ├─ bootstrap.css
│ ├─ favicon.ico
│ └─ index.html
├─ src
│ ├─ App.jsx
│ ├─ components
│ │ ├─ List
│ │ │ ├─ index.css
│ │ │ └─ index.jsx
│ │ └─ Search
│ │ └─ index.jsx
│ └─ index.js
└─ yarn.lock
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
最终目录结构如上
⚠️注意:各 jsx 编写的时候需要注意
class 需要改成 className
style 的值需要使用双花括号的形式
img
标签,一定要添加alt
属性表示图片加载失败时的提示a
标签要添加rel="noreferrer"
属性,不然会有大量的警告出现
展示页面效果:
# 二、axios 发送请求
在实现静态组件之后,我们需要通过向 github
发送请求,来获取相应的用户信息
# 1 启动服务器
但短时间内多次请求,可能会导致请求不返回结果等情况发生,因此我们采用了一个事先搭建好的本地服务器
用天禹老师给我们的本地服务器,这个文件在 这里 (opens new window)
用vscode打开这文件,里面有 server.js
server
├─ node_modules
├─ server.js
├─ package.json
└─ yarn.lock
2
3
4
5
建议在vscode里启动终端,然后启动 sever1.js
服务器
npm start
此时服务器启动成功,有两个访问地址,/users是真的访问github,/users2是本地模拟数据
服务器启动成功
请求github真实数据请访问:http://localhost:5000/search/users
请求本地模拟数据请访问:http://localhost:5000/search/users2
2
3
这俩选择一个去请求数据就行
# 2 axios发送请求
# 2.1 按钮绑定事件
首先在 Search 的 index.jsx 里,对 button
按钮绑定事件 search
<button onClick={this.search}>搜索</button>
编写 serach
函数,需要获取用户的输入以及发送网络请求
search = () => {
// 获取用户的输入
// 发送网络请求
)
}
2
3
4
5
# 2.2 获取用户的输入
在需要触发事件的 input
标签中,添加 ref
属性,这里采用 02_面向组件编程/回调形式refs (opens new window)
<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索" />
在 search
函数中,我们通过连续的解构赋值,解构到 value
后,并将 value
重命名为 keyWord
const { keyWordElement: { value: keyWord } } = this
此时 value
被重命名为 keyWord
,获取到了 keyWord
值,接下来我们就需要发送请求了
console.log(keyWord) // 返回你输入框输入的内容
# 2.3 配置代理,发送网络请求
详情见 07_脚手架配置代理 (opens new window)
首先 我们需要在Reac t脚手架 src
目录下,创建代理配置文件 setupProxy.js
⚠️注意:这个文件只能叫这个名字,脚手架在启动的时候,会自动执行这些文件
然后 在 setupProxy.js
里配置代理
配置一个代理的完整代码如下:
// setupProxy.js
const {createProxyMiddleware} = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
createProxyMiddleware('/api1',{
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: {'^/api1': ''}
}),
)
}
2
3
4
5
6
7
8
9
10
11
在 search
函数中,调用 axios.get 发送网络请求
axios.get(`https://localhost:3000/api1/search/users?q=${keyWord}`).then(
response => {console.log('成功了',response.data)},
error => {console.log('失败了', error)}
)
2
3
4
我们将 keyWord
接在请求地址的后面,来传递参数,以获得相关数据
因为配置了代理来解决跨域的问题,我们需要在请求地址前(端口号后面)加上启用代理的标志 /api1
⚠️注意:
此时我们站在 3000 向 3000 发送请求,因为 3000 有代理,所以转发给 5000 从而解决了跨域的问题
当我们站在 3000 向 3000 发送请求时(站的位置就是要发请求的地方),其实可以简写如下
axios.get(`/api1/search/users?q=${keyWord}`).then()
这样我们就能成功的获取到了数据
# 三、渲染数据
在获取到了数据之后,我们需要对数据进行分析,并将这些数据渲染到页面上
# 1 初始化状态
目前的学习知识中,Search
和 List
属于兄弟组件,没有办法进行直接的数据传递,因此可以将数据传递给 APP 再由 APP 转发给子组件
父子组件之间传递参数的方式,可以通过 state
和 props
实现
我们通过在父组件也就是 App.jsx
中设置状态
// 初始化状态,users初始值
state = { users: [] }
2
# 2 数据传递函数
我们在 App.jsx
中添加 saveUsers
函数,这样可以将 Search
组件传递的参数,维护到 App
的状态中
// 保存users数据
saveUsers = (users) => {
this.setState({ users })
}
2
3
4
然后将这个函数通过 props
传递给 Search
组件
<Search saveUsers={this.saveUsers}/>
# 3 serach 函数
在 Search
组件的 serach
函数中,调用 saveUsers
函数,将 Search
组件传递的参数,维护在 App
的状态中
// Search inde.jsx
// 发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {this.props.saveUsers(response.data.items)},
error => {console.log('失败了', error)}
)
2
3
4
5
6
# 4 渲染数据
在 App
组件我们通过解构取出状态中的 users
const {users} = this.state
再将它通过 props
传递给 List
组件
<List users={users}/>
在 List
组件中,通过 map
遍历整个返回的数据,渲染用户数据
⚠️注意:我们获取到的用户个数是动态的,因此我们需要通过遍历的方式去实现
this.props.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>
)
})
2
3
4
5
6
7
8
9
10
⚠️注意:我们新建的 user
对象,一定要保证它的 id
的唯一性,用数据里的用户 id
作为 key={userObj.id}
同时将一些用户信息添加到其中,需要添加的信息有用户链接 html_url
、用户头像 avatar_url
、用户昵称 login
这样我们就能成功的渲染用户数据
# 四、增加交互
做到这里其实已经完成了一大半了,但是似乎少了点交互
第一次进入页面时 List 组件中的 欢迎使用字样
加载时的 Loading... 效果
在报错时应该 提示错误信息
这一些都预示着我们不能单纯的将用户数据直接渲染
我们需要添加一些判断,什么时候该渲染数据,什么时候渲染 loading,什么时候渲染 err
# 1 增加初始化状态
首先我们需要增加一些状态,来指示我们该渲染什么,比如
采用
isFrist
来判断页面是否第一次启动,初始值给true
,点击搜索后改为false
采用
isLoading
来判断是否应该显示 Loading 动画,初始值给false
,在点击搜索后改为true
,在拿到数据后改为false
采用
err
来判断是否渲染错误信息,当报错时填入报错信息,初始值给空
// 初始化状态
state = {
users: [], // users初始化为数组
isFirst:true, // 是否为第一次打开页面
isLoading:false, // 标识是否处于加载中
err:'', // 存储请求相关错误信息
}
2
3
4
5
6
7
# 2 重写数据传递函数
重写 saveUsers
函数方法为 updateAppState
,采用更新状态的方式,接收一个状态对象来更新数据
// 接收一个状态对象
updateAppState = (stateObj) => {
this.setState(stateObj)
}
2
3
4
⚠️注意:我们需要改变数据传递方式,这样就不用去指定什么时候更新什么,减少了很多不必要的函数声明
然后将这个函数通过 props
传递给 Search
组件
<Search updateAppState={this.updateAppState} />
# 3 重写 serach 函数
在 Search
组件的 search
函数中,多次调用 updateAppState
函数
// Search/index.jsx
search = () => {
// 获取用户的输入
const { keyWordElement: { value: keyWord } } = this
// 发送请求前通知App更新状态
this.props.updateAppState({ isFirst: false, isLoading: true })
// 发送网络请求
axios.get(`/api1/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 })
}
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我们的状态更新是在 Search
组件中实现的,在点击搜索之后
发送请求前通知 App 更新状态
isFirst: false, isLoading: true
请求成功后通知 App 更新状态
isLoading: false, users: response.data.items
请求失败通知 App 更新状态
isLoading: false, err: error.message
# 4 判断状态的值来渲染数据
在 App 组件给 List 组件传递数据时,我们可以采用批量传递的方式,将状态里的对象都传递给 List ,这样可以减少代码量
<List {...this.state} />
⚠️注意:此时之前的解构取出状态中的 users
就可以删掉了
然后我们只需要在 List 组件中,判断这些状态的值,来显示即可,这里采用连续三目运算符
// List/index.jsx
// 对象解构
const { users, isFirst, isLoading, err } = this.props
// 判断
{
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>
)
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
连续三目运算符判断:
- 我们需要先判断是否第一次
- 再判断是不是正在加载
- 再判断有没有报错
- 通过层层判断,如果不是第一次、不是正在加载、没有报错,最后再渲染数据
以上就是 Github 搜索案例的实现过程
最终效果图:
前端路还有很长,加油吧!!!
非常感谢您的阅读,欢迎提出你的意见,有什么问题欢迎指出,谢谢!🎈
← 07_脚手架配置代理 09_消息订阅发布 →