• 二、【React脚手架】组件化编码(TodoList案例)


    1、组件化编码流程(通用)

    1. 拆分组件: 拆分界面,抽取组件

    2. 实现静态组件: 使用组件实现静态页面效果

    3. 实现动态组件

      1. 动态显示初始化数据
        1. 数据类型
        2. 数据名称
        3. 保存在哪个组件?
      2. 交互(从绑定事件监听开始)

    2、样式覆盖问题

    1. 使用 less,例如import '.\xxx.less',样式采用嵌套格式,顶级样式名不重即可,建议使用组件名
      1. 也可使用2的方法,此时不必写 .module
    2. css样式文件加 .module,例如 xxx.module.css
      1. 用参数命名引入这个样式文件: import styles from 'xxx.module.css'
      2. 使用时采用react插值方式:className={styles.xx}

    3、DEMO

    3.1、需要实现的效果

    在这里插入图片描述

    3.2、前期须知

    • 如果要对 props 进行限制,需要引入 prop-types 库

    • id 需要使用不重复随机数,推荐使用nanoid

      • 安装指令:yarn add nanoid

      • 引入函数:import { nanoid } from 'nanoid'

      • 使用函数:nanoid()

    3.3、项目结构

    只有红框内的文件有修改或是新增的

    在这里插入图片描述

    3.4、CODE

    3.4.1、App.js

    import React, { Component } from 'react'
    import { nanoid } from 'nanoid'
    import Header from './components/ToDoList/Header'
    import List from './components/ToDoList/List'
    import Footer from './components/ToDoList/Footer'
    import './App.css'
    
    export default class App extends Component {
    
      state = {
        todoList: [{
          id: '001',
          name: '吃饭',
          done: false
        }, {
          id: '002',
          name: '睡觉',
          done: false
        }, {
          id: '003',
          name: '打豆豆',
          done: false
        }]
      }
    
      toNever = id => {
        const { todoList } = this.state
        todoList.map(item => {
          if (item.id === id) {
            item.done = !item.done
          }
          return item
        })
        this.setState({ todoList })
      }
    
      addOne = name => {
        const { todoList } = this.state
        this.setState({
          todoList: [
            {
              id: nanoid(),
              name,
              done: false
            },
            ...todoList
          ]
        })
      }
    
      deleteById = id => {
        if (!window.confirm('确定删除吗?')) return
        const { todoList } = this.state
        this.setState({ todoList: todoList.filter(item => item.id !== id) })
      }
    
      deleteDone = () => {
        if (!window.confirm('确定删除吗?')) return
        const { todoList } = this.state
        this.setState({ todoList: todoList.filter(item => !item.done) })
      }
    
      doneAllOrNot = () => {
        const { todoList } = this.state
        const dones = todoList.filter(i => i.done).length
    
        todoList.map(item => {
          item.done = !(todoList.length === dones)
          return item
        })
        this.setState({ todoList })
      }
    
      render() {
        return (
          <div className="todo-container" >
            <div className="todo-wrap">
              <Header addOne={this.addOne} />
              <List {...this.state} toNever={this.toNever} deleteById={this.deleteById} />
              <Footer {...this.state} doneAllOrNot={this.doneAllOrNot} deleteDone={this.deleteDone} />
            </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
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    3.4.2、App.css

    /*base*/
    body {
        background: #fff;
    }
    
    .btn {
        padding: 4px 12px;
        margin-bottom: 0;
        font-size: 14px;
        line-height: 20px;
        text-align: center;
        vertical-align: middle;
        cursor: pointer;
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
        border-radius: 4px;
    }
    
    .btn-danger {
        color: #fff;
        display: none;
        background-color: #da4f49;
        border: 1px solid #bd362f;
    }
    
    .btn-danger:hover {
        color: #fff;
        background-color: #bd362f;
    }
    
    .btn:focus {
        outline: none;
    }
    
    .todo-container {
        width: 600px;
        margin: 0 auto;
    }
    
    .todo-container .todo-wrap {
        padding: 10px;
        border: 1px solid #ddd;
        border-radius: 5px;
    }
    
    /*header*/
    .todo-header input {
        width: 560px;
        height: 28px;
        font-size: 14px;
        border: 1px solid #ccc;
        border-radius: 4px;
        padding: 4px 7px;
    }
    
    .todo-header input:focus {
        outline: none;
        border-color: rgba(82, 168, 236, 0.8);
        box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
    }
    
    /*main*/
    .todo-main {
        margin-left: 0px;
        border: 1px solid #ddd;
        border-radius: 2px;
        padding: 0px;
    }
    
    .todo-empty {
        height: 40px;
        line-height: 40px;
        border: 1px solid #ddd;
        border-radius: 2px;
        padding-left: 5px;
        margin-top: 10px;
    }
    
    /*item*/
    li {
        list-style: none;
        height: 36px;
        line-height: 36px;
        padding: 0 5px;
        border-bottom: 1px solid #ddd;
        display: flex;
        justify-content: space-between;
    }
    
    li label {
        float: left;
        cursor: pointer;
    }
    
    li label li input {
        vertical-align: middle;
        margin-right: 6px;
        position: relative;
        top: -1px;
    }
    
    li button {
        float: right;
        display: none;
        margin-top: 3px;
    }
    
    li:hover .btn {
        display: block;
    }
    
    li:before {
        content: initial;
    }
    
    li:last-child {
        border-bottom: none;
    }
    
    /*footer*/
    .todo-footer {
        height: 40px;
        line-height: 40px;
        padding-left: 6px;
        margin-top: 5px;
    }
    
    .todo-footer label {
        display: inline-block;
        margin-right: 20px;
        cursor: pointer;
    }
    
    .todo-footer label input {
        position: relative;
        top: -1px;
        vertical-align: middle;
        margin-right: 5px;
    }
    
    .todo-footer button {
        float: right;
        margin-top: 5px;
    }
    
    .todo-footer:hover .btn {
        display: block;
    }
    
    • 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
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    3.4.3、Header

    import React, { Component } from 'react'
    import PropTypes from 'prop-types'
    
    export default class Header extends Component {
    
        static propTypes = {
            addOne: PropTypes.func.isRequired
        }
    
        add = e => {
            const { addOne } = this.props
            if (e.key === 'Enter') {
                if (!!e.target.value.trim()) {
                    addOne(e.target.value)
                    e.target.value = ''
                } else {
                    alert('请输入非空任务名')
                }
            }
        }
        render() {
            return (
                <div className="todo-header">
                    <input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.add} />
                </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

    3.4.4、List

    import React, { Component } from 'react'
    import Item from './Item'
    
    export default class List extends Component {
    
        render() {
            const { todoList, toNever, deleteById } = this.props
            return (
                <ul className="todo-main">
                    {
                        todoList.map(item => <Item key={item.id} todo={item} toNever={toNever} deleteById={deleteById} />)
                    }
                </ul>
            )
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.4.5、Item

    import React, { Component } from 'react'
    
    export default class Item extends Component {
        render() {
            const { id, name, done } = this.props.todo
            const { toNever, deleteById } = this.props
            return (
                <li>
                    <label>
                        <input type="checkbox" checked={done} onChange={() => toNever(id)} />
                        <span>{name}</span>
                    </label>
                    <button className="btn btn-danger" onClick={() => deleteById(id)}>删除</button>
                </li>
            )
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.4.6、Footer

    import React, { Component } from 'react'
    
    export default class Footer extends Component {
        render() {
            const { todoList, doneAllOrNot, deleteDone } = this.props
            const total = todoList.length
            const done = todoList.filter(item => item.done).length
            return (
                <div className="todo-footer">
                    <label>
                        <input type="checkbox" checked={!!(total || 0) && total === done} onChange={doneAllOrNot} />
                    </label>
                    <span>
                        <span>已完成{done || 0}</span> / 全部{total || 0}
                    </span>
                    <button className="btn btn-danger" onClick={deleteDone}>清除已完成任务</button>
                </div>
            )
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20


    小白学习参考视频:尚硅谷React教程

    中文官网:React 官方中文文档

  • 相关阅读:
    IDEA插件Mybatis Log Plugin的安装及其使用教程
    如何运用并行编程Parallel提升任务执行效率
    css 固定图片尺寸16:9
    ②. GPT错误:图片尺寸写入excel权限错误
    Dapr学习积累(一)
    杂谈-Android和Ios的对比
    Hot Door CADtools13 for Illustrator 2019-2022
    展馆导览系统之AR互动式导航与展品语音讲解应用
    通过解析库探究函数式抽象代价
    Visual Studio中vim模拟器
  • 原文地址:https://blog.csdn.net/qq_30769437/article/details/128080331