react实战项目之GitHub用户搜索项目

一、项目效果图 

     项目思路:可以将如下页面划分为Search、List两个组件,App组件用来组合两个组件。

     项目难点:跨域、Bootstrap框架引用、axios请求

     项目注意点:设计状态时考虑要全面,比如List组件必须考虑到网络请求成功、失败等情况,还有个用来显示loading的信息等情况。

二、编写各个组件,明确各个组件返回的html以及各个组件的css样式

1.项目引入自编css

    放在src/components目录下,各个组件的文件夹内;组件内使用时直接import即可;记得需要加后缀.css。只有后缀js和jsx不需要写。

import './App.css'

2.项目引入第三方css文件

   (1)放在public/css目录下;使用时在index.html 通过link引入

相对当前路径
<link src="./css/bootstrap.css"/>

相对根路径 react项目根路径为public文件夹 常用
<link src="/css/bootstrap.css"/>

绝对路径 只有react项目才可以识别
<link src="%PUBLIC_URL%/css/bootstrap.css"/>

  (2)npm或者yarn安装css库,然后import导入 

1)可以在react项目中执行以下命令安装bootstrap

npm install bootstrap@3 --save


2)使用bootstrap的组件中导入组件库和css样式库

import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap';

3)因为bootstrap依赖jQuery,在react的index.html文件中加个jQuery的引入

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>

三、动态获取数据,渲染各个组件

1.解构赋值

     解构语法为: const {属性名} = 对象

(1)多层对象的连续解构赋值
   let obj = {a:{b:{c:"Hello"}}}
   const {a:{b}:{c}} = obj //输出c值为 Hello
   console.log(a)  //a 或者 b没有值

   连续解构赋值且对属性重命名,比如将上面的c重命名为data
   const {a:{b}:{c:data}} = obj 
   console.log(data) 此时c属性重命名为data
   
(2)单层对象的解构赋值
   let obj = {a:'hello',b:'world'}

   const {a,b} = obj //将对象obj的属性全部赋值给a b

2.axios发送个体请求

(1)安装axios
  npm i axios
(2)发送请求   注意get中url为反引号
 import axios from 'axios'
   axios.get(`http://ip:port/getUserName/${userId}`).then(
     response=>{console.log('输出成功了',response.data)},
     error=>{console.log('失败了',error)}
 )

3.JSX判断语句

   JSX不允许使用if语句,可以连续使用三目运算符来实现判断 

   a?b:c?d:f   依次判断来决定值是哪个

export default class List extends Component {
    render() {
        const { users, isFirst, isError, isLoading, err } = this.props
        return (
            <div className="row">
                {
                    isFirst ? < h2 > 请输入名字进行查询</h2> :
                        isLoading ? < h2 > Loading....</h2 > :
                            isError ? err :
                                users.map(
                                    user => {
                                        return (
                                            <div key={user.id} className="card">
                                                <a href={user.url} target="_blank" rel="noreferrer" >
                                                    <img alt='替代图片' src={user.avatar_url} style={{ width: '100px' }} />
                                                </a>
                                                <p className="card-text">{user.login}</p>
                                            </div>)
                                    }
                                )
                }
            </div>
        )
    }
}

4.项目数据传递思路

     axios在Search组件发送数据,获得数据通过调用父组件App通过props传递过来的函数更新App组件的state,然后App通过props将state传给List组件。相当于实现了父子组件传递、子父组件传递数据。问题是,Search组件直接将数据传给List组件不好吗?为甚要借助父组件作为中间进行数据传递呢?

 四、消息订阅-发布机制

(1)作用:用来实现任意组件之间的数据传递。 以前学习的都是通过props实现父子组件传递。

(2)使用步骤

  1. 工具库 PubSubJS 。可在github中搜索查看具体用法。
  2. 下载   npm install pubsub-js --save
  3. 使用

         componentDidMount中订阅消息,然后获取数据后调用组件setState方法重新渲染组件;需要发布消息的地方发布消息。

注:一般在组件的componentDidMount中订阅消息,在componentWillUnMount中取消订阅;npm 库中包名规范中都是小写的,所以引入时是pubsub-js 小写,不是PubSubJS

import PubSub from 'pubsub-js' //引入

//subscribe接收两个参数:topic和回调函数 回调函数接收两个参数msg和数据,其中msg记录了topic,很多情况下没必要使用。
const token = PubSub.subscribe('delete', function(msg,data){ }); //订阅 
PubSub.publish('delete', data) //发布消息

PubSub.unsubscribe(token) //取消订阅

注:subscribe回调函数必须接收两个参数,如果msg不使用,那么可以通过 _ 占位符来去掉msg。 

const token = PubSub.subscribe('delete', function(_,data){ }); //订阅

如下实习Search、List兄弟组件的通信 

Search组件

import React, { Component } from 'react'
import PubSub from 'pubsub-js' //引入
import axios from 'axios'

export default class Search extends Component {
    searchUsers = () => {
        const { keyWrodEle: { value: keyWorld } } = this
        PubSub.publish("userTopic", { isLoading: true, isFirst: false })
        axios.get(`https://api.github.com/search/users?q=${keyWorld}`).then(
            rsp => {
                console.log('成功了', rsp.data)
                PubSub.publish("userTopic", { isLoading: false, isError: false, users: rsp.data.items, err: '' })
            },
            error => {
                console.log('失败报错', error);
                PubSub.publish("userTopic", { isLoading: false, isError: true, err: error })
            }
        )

    }
    render() {
        return (
            <section className="jumbotron">
                <h3 className="jumbotron-heading">Search Github Users</h3>
                <div>
                    <input ref={c => this.keyWrodEle = c} type="text" placeholder="enter the name you search" />
                    <button onClick={this.searchUsers}>Search</button>
                </div>
            </section>
        )
    }
}

List组件 

import React, { Component } from 'react'
import PubSub from 'pubsub-js' //引入
import './index.css'

export default class List extends Component {
    state = { users: [], isFirst: true, err: '', isLoading: false, isError: false }

    componentDidMount() {
        PubSub.subscribe("userTopic", (_, data) => {
            this.setState(data)
        })
    }
    render() {
        const { users, isFirst, isError, isLoading, err } = this.state
        return (
            <div className="row">
                {
                    isFirst ? < h2 > 请输入名字进行查询</h2> :
                        isLoading ? < h2 > Loading....</h2 > :
                            isError ? err :
                                users.map(
                                    user => {
                                        return (
                                            <div key={user.id} className="card">
                                                <a href={user.url} target="_blank" rel="noreferrer" >
                                                    <img alt='替代图片' src={user.avatar_url} style={{ width: '100px' }} />
                                                </a>
                                                <p className="card-text">{user.login}</p>
                                            </div>)

                                    }
                                )

                }

            </div>
        )
    }
}

App组件 用来组合组件

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
import './App.css'
export default class App extends Component {
  render() {
    return (
      <div className="container">
        <Search />
        <List />
      </div>
    )
  }
}

 五、扩展:前端发送网络请求

      前端发送网络请求有XMLHttpRequest和fetch两种JS原生支持方式。ajax、jquery、axios都是基于XMLHttpRequest来扩展的。axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。

fetch发送请求 

         因为是JS原生支持,不需要引入既可以使用。但是fetch是关注分离的设计模式,所以第一个then的rsp表示是否握手服务器成功;rsp.json()返回服务器请求的数据,是一个promise对象。然后第二个then表示获取到的数据 。catch对错误统一处理。

      fetch对老版本浏览器兼容不好,故不怎么使用。

      fetch(`https://api.github.com/search/users?q=${keyWorld}`).then(
               rsp => {
                   console.log('服务器握手成功了')
                   return rsp.json()
               },
           ).then(
               rsp => {
                   console.log("获取数据成功", rsp)
               },
           ).catch(
               err => {
                   console.log("失败", err);
               }
           )

fetch结合await发送请求

    searchUsers = async() => {
  
        try {
            const rsp = await fetch(`https://api.github.com/search/users?q=${keyWorld}`)
            const data = await rsp.json()
            console.log("成功收到数据", data)
        } catch (error) {
            console.log('错误', error)
        }
    }

//#region  //#endregion 这个语法可以将代码整体收缩起来。 

//#region fetch发送网络请求 未优化
   ...代码段
//#endregion

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fang·up·ad

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

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

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

打赏作者

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

抵扣说明:

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

余额充值