Marvel-Site Marvel-Site
首页
  • Java

    • Java基础
    • Java进阶
    • Java容器
    • Java并发编程
    • Java虚拟机
  • 计算机基础

    • 数据结构与算法
    • 计算机网络
    • 操作系统
    • Linux
  • 框架|中间件

    • Spring
    • MySQL
    • Redis
    • MQ
    • Zookeeper
    • Git
  • 架构

    • 分布式
    • 高并发
    • 高可用
    • 架构
  • 框架

    • React
    • 其他
  • 实用工具
  • 安装配置

    • Linux
    • Windows
    • Mac
  • 开发工具

    • IDEA
    • VsCode
  • 关于
  • 收藏
  • 草稿
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)

Marvel

吾必当乘此羽葆盖车
首页
  • Java

    • Java基础
    • Java进阶
    • Java容器
    • Java并发编程
    • Java虚拟机
  • 计算机基础

    • 数据结构与算法
    • 计算机网络
    • 操作系统
    • Linux
  • 框架|中间件

    • Spring
    • MySQL
    • Redis
    • MQ
    • Zookeeper
    • Git
  • 架构

    • 分布式
    • 高并发
    • 高可用
    • 架构
  • 框架

    • React
    • 其他
  • 实用工具
  • 安装配置

    • Linux
    • Windows
    • Mac
  • 开发工具

    • IDEA
    • VsCode
  • 关于
  • 收藏
  • 草稿
  • 索引

    • 分类
    • 标签
    • 归档
GitHub (opens new window)
  • 基础

  • 框架

    • React

      • React入门
      • React组件
      • DOM的Diffing算法
      • React脚手架介绍
      • React简单案例
        • TodoList案例
          • 展示效果
          • 安装库
          • 相关知识点
          • 代码
        • Github搜索用户案例
          • 展示效果
          • 代码Axios
          • 代码Pubsub
      • React路由
      • redux
      • 井字棋
    • 其他

  • Node

  • 前端
  • 框架
  • React
Marvel
2023-08-21
目录

React简单案例

# React简单案例

# TodoList案例

# 展示效果

image-20230821171217951

拆分组件:将整个应用拆成多个组件

  • Header:页眉输入框
  • List:列表展示
  • Item:列表中的每一项
  • Footer:页脚部分

# 安装库

npm i nanoid            # 用于产生唯一id
npm i prop-types		# 用于限制props类型、必要性
1
2

# 相关知识点

  1. 拆分组件、实现静态组件,注意:className、style 的写法
  2. 动态初始化列表,如何确定将数据放在哪个组件的 state 中?
    • 某个组件使用:放在自身的state中
    • 某些组件使用:放在他们共同的父组件 state 中(官方称此操作为:状体提升)
  3. 关于父子之间通信:
    • 【父组件】给【子组件】传递数据:通过props传递
    • 【子组件】给【父组件】传递数据:通过props传递,要求父组件提前给子组件传递一个函数
  4. 注意 defaultChecked 和 checked的区别,类似的还有:defaultvalue 和 value
  5. 状态在哪里,操作状态的方法就在哪里

# 代码

css样式代码省略了

src/App.jsx

import React,{Component} from 'react'
import Header from './components/Header'
import Footer from './components/Footer'
import List from './components/List'
import './App.css'


export default class App extends Component{
  // 状态在哪里,操作状态的方法就在哪里

  // 初始化状态
  state = {todos:[
      {id:'001', name:'吃饭', done:true},
      {id:'002', name:'睡觉', done:true},
      {id:'003', name:'工作', done:false},
      {id:'004', name:'逛街', done:false},
  ]}
  // 用于添加todo,接受参数是todo对象
  addTodo = (todoObj) => {
    const {todos} = this.state
    const newTodos = [todoObj,...todos]
    this.setState({todos:newTodos})
  }
  // 用于更新一个TODO对象
  updateTodo = (id, done) => {
    const {todos} = this.state
    // 匹配处理数据
    const newTodos = todos.map((todoObj)=>{
      if(todoObj.id === id) return {...todoObj,done}
      else return todoObj
    })
    this.setState({todos:newTodos})
  }
  // 用于删除一个todo对象
  deleteTodo = (id) => {
    const {todos} = this.state
    const newTodos = todos.filter((todoObj) => {
      return todoObj.id !== id
    })
    this.setState({todos:newTodos})
  }
  // 全选todo对象
  checkAllTodo = (done) => {
    const {todos} = this.state
    const newTodos = todos.map((todoObj)=>{
      return {...todoObj, done:done}
    })
    this.setState({todos:newTodos})
  }
  // 清除所有已完成
  clearAllDone = () => {
    const {todos} = this.state
    const newTodos = todos.filter((todoObj)=>{
      return !todoObj.done
    })
    this.setState({todos:newTodos})
  }
  render(){
    return (
      <div className='todo-container'>
        <Header addTodo={this.addTodo}/>
        <List todos={this.state.todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/>
        <Footer todos={this.state.todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/>
      </div>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

src/components/Footer/index.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'

export default class Footer extends Component {
  // 对接收的props进行类型、必要性的限制
  static propTypes = {
    addTodo:PropTypes.func.isRequired
  }
  handleCheckAll = (event) => {
    this.props.checkAllTodo(event.target.checked)
  }
  handleClearAllDone = () => {
    this.props.clearAllDone()
  }
  render() {
    const {todos} = this.props
    const doneCount = todos.reduce((pre, cur) => {
      return pre + (cur.done ? 1 : 0)
    }, 0)
    const total = todos.length
    return (
      <div className='todo-footer'>
        <label>
          <input type='checkbox' onChange={this.handleCheckAll} checked={doneCount === total && total !== 0 ? true : false} />
        </label>
        <span>
          <span>已完成{doneCount}</span> / 全部{total}
        </span>
        <button onClick={this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button>

      </div>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

/src/components/Header/index.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'

export default class Header extends Component {
  // 对接收的props进行类型、必要性的限制
  static propTypes = {
    addTodo:PropTypes.func.isRequired
  }

  handleKeyUp = (event) => {
    // 结构赋值,回车是13
    const {keyCode, target} = event
    // 判断是否为回车按键
    if (keyCode !== 13) return
    // 添加的todo名字不能为空
    if (target.value.trim() === ''){
      alert('输入不能为空')
      target.value = ''
      return
    }
    // 准备好todo对象
    // npm install nanoid,通过nanoid生成唯一的id
    const todoObj = {id:nanoid(),name:target.value,done:false}
    // 子组件给父组件,通过调用父组件的函数
    this.props.addTodo(todoObj)
    // 清空输入
    target.value = ''
  }

  

  render() {
    return (
      <div className="todo-header">
        <input onKeyUp={this.handleKeyUp} type="text" placeholder='请输入你的任务名称,按回车键确认' />
      </div>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/src/components/Item

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'

export default class Item extends Component {
  // 对接收的props进行类型、必要性的限制
  static propTypes = {
    updateTodo:PropTypes.func.isRequired,
    deleteTodo:PropTypes.func.isRequired
  }
  state = {mouse:false}
  // 鼠标移入移除回调
  handleMouse = (flag) => {
    return () => {
      this.setState({mouse:flag})
    }
  }
  // 勾选或取消勾选
  handleCheck = (id) => {
    return (event)=> {
      this.props.updateTodo(id, event.target.checked)
    }
  }
  // 删除一个tod (不用高阶函数实现)
  handleDelete = (id) => {
    if (window.confirm('确认删除吗?')){
      this.props.deleteTodo(id)
    }
  }
  render() {
    const {id,name,done} = this.props
    const {mouse} = this.state
    return (
      <li style={{background:mouse ? "#ddd" : "white"}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
        <label>
          <input type='checkbox' checked={done} onChange={this.handleCheck(id)}/>
          <span>{name}</span>
        </label>
        <button onClick={() => {this.handleDelete(id)}} className='btn btn-danger' style={{display:mouse?'block':'none'}}>删除</button>
      </li>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

/src/components/List

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'

export default class List extends Component {
  // 对接收的props进行类型、必要性的限制
  static propTypes = {
    todos:PropTypes.array.isRequired,
    updateTodo:PropTypes.func.isRequired,
    deleteTodo:PropTypes.func.isRequired
  }
  render() {
    const {todos} = this.props
    return (
      <ul className="todo-main">
        {
          todos.map((todo) => {
            return <Item key={todo.id} {...todo} updateTodo={this.props.updateTodo} deleteTodo={this.props.deleteTodo}/>
          })
        }
      </ul>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# Github搜索用户案例

# 展示效果

image-20230824134554922 image-20230824143013230

输入用户点击搜索后,即可看到用户列表,点击用户头像可跳转到用户Github主页。

# 代码Axios

数据的传输方式:父传给子

设置代理:/src/setupProxy.js

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

module.exports = function(app){
    app.use(
        createProxyMiddleware('/api1',{
            target:'https://api.github.com',
            changeOrigin:true,
            pathRewrite:{'^/api1':''}
        })

    )
}
1
2
3
4
5
6
7
8
9
10
11
12

APP:/src/App.jsx

//创建外壳组件APP
import React,{Component} from 'react'
import Header from './components/header/header';
import List from './components/list/list'

export default class App extends Component{
    state = {
        Git:[],
        isFrist:true,
        isLoad:false,
        isError:''
    }

    updateAppState = (stateObj) =>{
        this.setState(stateObj) 
    }
    
    render(){
        //通过 ...将状态中的全部赋值过去
        return ( 
            <div className="container">
                <Header updateAppState = {this.updateAppState} />
                <List {...this.state} />
            </div>    
            
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

搜索框:/src/components/header/header.jsx

import React, { Component } from 'react'
import axios from 'axios';

export default class Header extends Component {

    search = () =>{
        //const {value} = this.KeyValue;
        //连续解构赋值,拿到this下面的KeyValue中的value,并进行重命名为KeyWord
        const {KeyValue:{value:keyWord}} = this;

        //在搜索之前设置,搜索的开始,结束第一次展示
        this.props.updateAppState({isFrist:false,isLoad:true})

        //切记在配置代理了之后一定需要添加相应的路径
        axios.get(`http://localhost:3000/api1/search/users?q=${keyWord}`).then(
            success => {
                this.props.updateAppState({Git:success.data.items,isLoad:false});
            },
            error =>    {
                this.props.updateAppState({isError:error.message,isLoad:false});
            }
        )
    }

    render() {
        return (
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索GitHub用户</h3>
                <div>
                    {/*使用ref拿到输入的数据,ref中使用回调函数的形式,在实例对象中创建一个KeyValue的属性,值是该节点*/}
                    <input ref={ c => this.KeyValue = c} type="text" placeholder="输入关键词进行搜索"/>&nbsp;
                    <button onClick = {this.search}>搜索</button>
                </div>
            </section>
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

展示列表:/src/components/list/list.jsx

import React, { Component } from 'react';
import './list.css'

export default class List extends Component {

    render() {
        const {Git,isFrist,isLoad,isError} = this.props;
        return (
            <div className="row">
                {
                    //因为不能在JSX语法中使用if,只能是表达式,所以可以是有用三元运算符进行判断。
                    isFrist ? <h1>欢迎进入页面</h1> : 
                    isLoad ? <h2>正在搜索页面</h2> :
                    isError !== ''? <h1>{isError}</h1> :
                    //注意传递过来的一定是一个数组,不能是一个对象
                    //要不然初始化的时候对象就是 undefined 在遍历的时候就会出错
                    Git.map((git)=>{
                        return (
                            <div className="card" key = {git.id}>
                                <a href={git.html_url} target="_blank" rel="noreferrer">
                                <img alt="headImg" src={git.avatar_url} style={{width:'100px'}}/>
                                </a>
                                <p className="card-text">{git.login}</p>
                            </div>
                            )
                    })
                }
                
                
            </div>
        );
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 代码Pubsub

工具库:PubSubJS

下载:npm install pubsub-js --save

Github:https://github.com/mroderick/PubSubJS

/src/App.jsx

import React,{Component} from 'react'
import Header from './components/header/header';
import List from './components/list/list'

export default class App extends Component{

    render(){
        //通过 ...将状态中的全部赋值过去
        return ( 
            <div className="container">
                <Header />
                <List />
            </div>    
            
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/src/components/header/header.jsx

import React, { Component } from 'react'
import PubSub from 'pubsub-js';
import axios from 'axios';

export default class Header extends Component {

    search = () =>{
        //连续解构赋值,拿到this下面的KeyValue中的value,并进行重命名为KeyWord
        const {KeyValue:{value:keyWord}} = this;

        //在搜索之前设置,搜索的开始,结束第一次展示
        PubSub.publish('search', {isFrist:false,isLoad:true})

        //切记在配置代理了之后一定需要添加相应的路径
        axios.get(`http://localhost:3000/api1/search/users?q=${keyWord}`).then(
            success => {
                PubSub.publish('search', {Git:success.data.items,isLoad:false})
            },
            error =>    {
                PubSub.publish('search', {isError:error.message,isLoad:false})
            }
        )
    }

    render() {
        return (
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索GitHub用户</h3>
                <div>
                    {/*使用ref拿到输入的数据,ref中使用回调函数的形式,在实例对象中创建一个KeyValue的属性,值是该节点*/}
                    <input ref={ c => this.KeyValue = c} type="text" placeholder="输入关键词进行搜索"/>&nbsp;
                    <button onClick = {this.search}>搜索</button>
                </div>
            </section>
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

/src/components/list/list.jsx

import React, { Component } from 'react';
import PubSub from 'pubsub-js';
import './list.css'

export default class List extends Component {
    state = {
        Git:[],
        isFrist:true,
        isLoad:false,
        isError:''
    }

    componentDidMount() {
        this.token = PubSub.subscribe('search', (_,stateObj)=>{
            this.setState(stateObj)
        })
    }

    componentWillUnmount() {
        PubSub.unsubscribe(this.token)
    }

    render() {
        const {Git,isFrist,isLoad,isError} = this.state;
        return (
            <div className="row">
                {
                    //因为不能在JSX语法中使用if,只能是表达式,所以可以是有用三元运算符进行判断。
                    isFrist ? <h1>欢迎进入页面</h1> : 
                    isLoad ? <h2>正在搜索页面</h2> :
                    isError !== ''? <h1>{isError}</h1> :
                    //注意传递过来的一定是一个数组,不能是一个对象
                    //要不然初始化的时候对象就是 undefined 在遍历的时候就会出错
                    Git.map((git)=>{
                        return (
                            <div className="card" key = {git.id}>
                                <a href={git.html_url} target="_blank" rel="noreferrer">
                                <img alt="headImg" src={git.avatar_url} style={{width:'100px'}}/>
                                </a>
                                <p className="card-text">{git.login}</p>
                            </div>
                            )
                    })
                }
            </div>
        );
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
编辑 (opens new window)
#前端#React
上次更新: 2023/08/24, 15:52:58
React脚手架介绍
React路由

← React脚手架介绍 React路由→

最近更新
01
位运算
05-21
02
二叉树
05-12
03
Spring三级缓存解决循环依赖
03-25
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Marvel
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式