0303github搜索案例-react应用

1 前言

学习了脚手架配置axios,我们通过访问github的接口来做个练习。实例效果如下所示:

在这里插入图片描述

搜索框输入github用户名关键字,点击搜索,列表展示对应的头像和用户名,点击跳转github主页。

基于我们访问github访问慢或者根本打不开的情况,我们通过nodejs,简单的写了个后端服务,代码1-1如下:

const express = require("express")
const axios = require("axios")
const app = express()


/*
  请求地址: http://localhost:3000/search/users?q=aa

  后台路由
    key: /search/users
    value: function () {}
*/
app.get("/search/users", function (req, res) {
  const {q} = req.query
  axios({
    url: 'https://api.github.com/search/users',
    params: {q}
  }).then(response => {
    res.json(response.data)
  })
})

app.get("/search/users2", function (req, res) {
  res.json({
    items: [
      {
        login: "yyx990803",
        html_url: "https://github.com/yyx990803",
        avatar_url:
          "https://avatars3.githubusercontent.com/u/499550?s=460&u=de41ec9325e8a92e281b96a1514a0fd1cd81ad4a&v=4",
        id: 1,
      },
      {
        login: "ruanyf",
        html_url: "https://github.com/ruanyf",
        avatar_url: "https://avatars2.githubusercontent.com/u/905434?s=460&v=4",
        id: 2,
      },
      {
        login: "yyx9908032",
        html_url: "https://github.com/yyx990803",
        avatar_url:
          "https://avatars3.githubusercontent.com/u/499550?s=460&u=de41ec9325e8a92e281b96a1514a0fd1cd81ad4a&v=4",
        id: 3,
      },
      {
        login: "ruanyf2",
        html_url: "https://github.com/ruanyf",
        avatar_url: "https://avatars2.githubusercontent.com/u/905434?s=460&v=4",
        id: 4,
      },
      {
        login: "yyx9908033",
        html_url: "https://github.com/yyx990803",
        avatar_url:
          "https://avatars3.githubusercontent.com/u/499550?s=460&u=de41ec9325e8a92e281b96a1514a0fd1cd81ad4a&v=4",
        id: 5,
      },
      {
        login: "ruanyf3",
        html_url: "https://github.com/ruanyf",
        avatar_url: "https://avatars2.githubusercontent.com/u/905434?s=460&v=4",
        id: 6,
      },
      {
        login: "yyx9908034",
        html_url: "https://github.com/yyx990803",
        avatar_url:
          "https://avatars3.githubusercontent.com/u/499550?s=460&u=de41ec9325e8a92e281b96a1514a0fd1cd81ad4a&v=4",
        id: 7,
      },
      {
        login: "ruanyf4",
        html_url: "https://github.com/ruanyf",
        avatar_url: "https://avatars2.githubusercontent.com/u/905434?s=460&v=4",
        id: 8,
      },
      {
        login: "yyx9908035",
        html_url: "https://github.com/yyx990803",
        avatar_url:
          "https://avatars3.githubusercontent.com/u/499550?s=460&u=de41ec9325e8a92e281b96a1514a0fd1cd81ad4a&v=4",
        id: 9,
      },
    ],
  });
});



app.listen(5000, "localhost", (err) => {
  if (!err){
  	console.log("服务器启动成功")
  	console.log("请求github真实数据请访问:http://localhost:5000/search/users")
  	console.log("请求本地模拟数据请访问:http://localhost:5000/search/users2")
  } 
  else console.log(err);
})

  • 如果正常访问github接口https://api.github.com/search/users网络延迟高或者打不开,可以切换为我们模拟的接口。

2 静态组件

2.1 静态页面和样式

页面简单的包含搜索组件和列表展示组件,静态html和css代码如下2-1所示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="./bootstrap.css">
  <link rel="stylesheet" href="./index.css">
</head>
<body>
<div id="app">
  <div class="container">
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search"/>&nbsp;<button>Search</button>
      </div>
    </section>
    <div class="row">
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
    </div>
  </div>
</div>
</body>
</html>

样式使用bootstrap的样式,我们直接放入public\css下面,在主页index.html引入

我们自定义的样式,自定义样式代码如下:

.album {
  min-height: 50rem; /* Can be removed; just added for demo purposes */
  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%;
}

2.2 静态组件

使用脚手架创建项目后,去除不需要的文件,其中public/css 下放置bootstrap.css,在index.html中引入,代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <link rel="stylesheet" href="./css/bootstrap.css">
  <title>React 脚手架</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

src目录下创建2个组件:

  • Search组件:对应页面搜索部分;
  • List组件:对应列表展示部分。

App.jsx代码2.2-2如下所示:

import React, { Component } from 'react'
import './app.css'
import Search from './component/Search'
import List from './component/List'


export default class App
  extends Component {

  render() {

    // const { githubUsers } = this.state

    return (
      <div>
        <div className="container">
          <Search />
          <List />
        </div>
      </div>
    )
  }
}

Seach组件初始状态代码2.2-3如下所示:

import React, { Component } from 'react'

export default class Search extends Component {

  render() {

    return (
      <section className="jumbotron">
        <h3 className="jumbotron-heading">搜索github用户</h3>
        <div>
          <input type="text" placeholder="输入用户名" />&nbsp;
          <button>搜索</button>
        </div>
      </section>
    )
  }
}

List组件初始状态代码2.2-4如下所示:

import React, { Component } from 'react'

export default class List extends Component {
  render() {
    const { isFirst, isLoading, githubUsers, err } = this.props
    return (
      <div className="row">
        <div className="card" >
          <a target="_blank">
            <img alt='avatar' src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{ width: '100px' }} />
          </a>
          <p className="card-text">xxx</p>
        </div>
        <div className="card" >
          <a target="_blank">
            <img alt='avatar' src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{ width: '100px' }} />
          </a>
          <p className="card-text">xxx</p>
        </div>
        <div className="card" >
          <a target="_blank">
            <img alt='avatar' src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{ width: '100px' }} />
          </a>
          <p className="card-text">xxx</p>
        </div>
      </div>
    )
  }
}

  • 修改类名和样式以后不在提示

3 axios请求github接口

我们自定义样式,都是些的List组件的样式,我们直接把样式放置在List对应的组件处,List组件引入样式。

流程梳理:search请求github接口,返回数据;把数据传递给List组件用于展示。

Search组件和List组件属于兄弟组件,传值目前我们需要通过其父组件App来传递。

第一步:App组件初始化用户数组状态

  state = { 
    githubUsers: [], // 初始化状态,github用户
  } 

第二步:Search请求后端接口,后端请求github接口返回数据;数据返回后,把数据传递给App。代码3-2如下所示:

// Search组件
import React, { Component } from 'react'
import axios from 'axios'

export default class Search extends Component {

  search = () => {
    // 获取用户输入
    const {keyElement: {value: keyword}} = this
    // 发起网络请求
    axios.get(`/api1/search/users?q=${keyword}`).then(
      resp => {
        // console.log('请求成功',resp.data);
        const data = resp.data.items.map(item => {
          return {id: item.id, avatar: item.avatar_url, homePage: item.html_url, username: item.login}
        })
        this.props.updateAppState(data)
      },
      error => {
        // console.log('请求失败', err);
      }
    )
  }
  
  render() {

    return (
      <section className="jumbotron">
        <h3 className="jumbotron-heading">搜索github用户</h3>
        <div>
          <input ref={c => this.keyElement = c} type="text" placeholder="输入用户名" />&nbsp;
          <button onClick={this.search}>搜索</button>
        </div>
      </section>
    )
  }
}

// App组件
  /**
   * 更新用户状态数据
   * @param {object} githubUsers 
   */
  updateAppState = (githubUsers) => {
    // 更新用户
    this.setState({githubUsers})
  }
<Search updateAppState={this.updateAppState}/>

第三步:App把数据传递给List,展示数据,代码3-3如下所示

// App 组件
<List {...this.state}/>
// List 组件
render() {
    const { isFirst, isLoading, githubUsers, err } = this.props
    return (
      <div className="row">
        {
           githubUsers.map(item => {
                return (
                  <div className="card" key={item.id}>
                    <a href={item.homePage} target="_blank" rel="noreferrer">
                      <img alt='avatar' src={item.avatar} style={{ width: '100px' }} />
                    </a>
                    <p className="card-text">{item.username}</p>
                  </div>
                )
              })
           }
      </div>
    )
  }

第四步:配置代理,setupProxy.js代码3-4如下

const { createProxyMiddleware } = require("http-proxy-middleware")

module.exports = function (app) {
  app.use(
    createProxyMiddleware("/api1",{
      target: "http://localhost:5000", //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: { "^/api1": "" }, 
    })
  )
}

4 优化案例

虽然点击搜索能实现效果了但是,存在一些问题:

  • 列表初始需要展示欢迎或者提示信息;
  • 当开始请求后,数据未返回前,展示加载信息;
  • 数据请求成功后,展示用户信息;数据请求失败,展示错误信息。

这时候我们需要拓展App中的状态数据如下所示:

  state = { 
    githubUsers: [], // 初始化状态,github用户
    isFirst: true, // 是否第一次打开页面
    isLoadign: false, // 数据是否加载中
    err: '', // 存储相关的错误信息
  } 

Search组件相应的做出调整如下:

  search = () => {
    // 获取用户输入
    const {keyelement: {value: keyword}} = this
    // console.log(keyword);
    // 发送请求前通知App更新状态
    this.props.updateAppState({isFirst: false, isLoading: true})
    // 发起网络请求
    axios.get(`/api1/search/users?q=${keyword}`).then(
      resp => {
        // console.log('请求成功',resp.data);
        const data = resp.data.items.map(item => {
          return {id: item.id, avatar: item.avatar_url, homePage: item.html_url, username: item.login}
        })
        this.props.updateAppState({isLoading: false, githubUsers: data})
      },
      error => {
        // console.log('请求失败', err);
        this.props.updateAppState({isLoading: false, err: error.message})
      }
    )
  }

List组件展示根据数据,调整展示如下:

render() {
    const { isFirst, isLoading, githubUsers, err } = this.props
    return (
      <div className="row">
        {
          isFirst ? <h2>欢迎使用,输入github用户名,点击搜索</h2> :
            isLoading ? <h2>Loading......</h2> :
              err ? <h2 style={{color: 'red'}}>{err}</h2> :
                githubUsers.map(item => {
                  return (
                    <div className="card" key={item.id}>
                      <a href={item.homePage} target="_blank" rel="noreferrer">
                        <img alt='avatar' src={item.avatar} style={{ width: '100px' }} />
                      </a>
                      <p className="card-text">{item.username}</p>
                    </div>
                  )
                })
        }
      </div>
    )
  }
  
  

关于脚手架设置代理请求真实网络数据小案例,讲解完毕。我们之前兄弟之间传递数据非常的麻烦,下面我们将要学习消息发布和订阅来解决这个问题。

结语

❓QQ:806797785

⭐️源代码仓库地址:https://github.com/gaogzhen/react-staging.git

参考:

[1]React视频教程[CP/OL].2020-12-15.p65-66.

[2]React官网[CP/OL].

[2]ChatGPT[CP/OL].

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值