美文网首页
bunny笔记|React-工程篇(实现todoList)

bunny笔记|React-工程篇(实现todoList)

作者: 一只小小小bunny | 来源:发表于2022-04-06 16:43 被阅读0次

    前言

    在企业中应用react进行项目开发,都是基于react的脚手架,我们称之为SPA(Singer Page Application)应用。在这些应用中,我们会用到路由,网络,状态管理等等全家桶体系的知识,也会进一步运用ES6/7语法,构架工具,架构,设置模式等。

    什么是React脚手架:
    1)react脚手架是用来帮助我们快速创建一个基于react库的模板项目,主要包括三部分:模板项目所有需要的配置,模板项目所有需要的依赖,安装/运行/编译环境,可以直接跑起来。
    2)使用脚手架开发的项目一定是要遵循模块化、组件化、工程化的;在react中提供了一个用于创建react项目的脚手架库:create-react-app。
    3)通常项目的整体技术配置是:react+react-??+webpack+ es6/7 + eslint.

    操作步骤:
    1)全局安装creact-react-app
    2)创建一个脚手架项目
    create-react-app (项目名称)demo cd (项目名称)demo npm start
    也可以用yarn (在React中大多数都是使用yarn来跑项目)

    React.png

    目录:

    • node_module
      • 各依赖
    • public
      • index.html
    • src
      • App.css/App.js/App.test.js
      • index.js/index.css
    • package-lock.json
    • package.json
    • README.md

    运行流程
    运行之后首先进入src下的index.js,然后进入App.js,再其次往下进入其它文件。

    为什么采用单向数据流:防止多个子组件同时修改数据

    React可以与三方框架并存。
    React只负责挂载的DOM节点,其它的节点可以运用其它的框架,react不会影响其使用。注意:要保证其它框架不会影响React的使用

    完成todolist案例

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById('root')
    );
    
    reportWebVitals();
    
    

    App.js

    import React, {Component} from 'react';
    import Head from './components/Head'
    import List from './components/List'
    import Foot from './components/Foot'
    
    class App extends Component {
        constructor(props) {
            super(props);
            this.state = {
                todos: [
                    {id: 1, title: '学习React课程', finished: false},
                    {id: 2, title: '刷半小时电视剧', finished: false},
                    {id: 3, title: '学习Java的课程', finished: false},
                    {id: 4, title: '学习Python的课程', finished: false},
                ],
                finishedCount: 0
            }
        }
    
        // 1. 修改完成状态
        changeTodoFinished = (todoId, isFinished) => {
            // 1.1 遍历
            const tempTodos = this.state.todos;
            let finishedCount = 0;
            tempTodos.forEach((todo, index) => {
                if (todo.id === todoId) {
                    todo.finished = isFinished;
                }
                if (todo.finished) {
                    finishedCount += 1;
                }
            });
    
            // 2.3 更新状态
            this.setState({
                todos: tempTodos,
                finishedCount
            })
        };
        // 2. 删除一条记录
        removeTodoWithId = (todoId) => {
            // 2.1 遍历
            const tempTodos = this.state.todos;
            let finishedCount = 0;
            tempTodos.forEach((todo, index) => {
                if (todo.id === todoId) {
                    tempTodos.splice(index, 1);
                }
            });
    
            // 2.2 处理选中的
            tempTodos.forEach((todo, index) => {
                if (todo.finished) {
                    finishedCount += 1;
                }
            });
    
            // 2.3 更新状态
            this.setState({
                todos: tempTodos,
                finishedCount
            })
        };
        // 3. 添加一个todo
        addOneTodo = (todo) => {
            // 3.1 取出数组
            let tempTodos = this.state.todos;
            tempTodos.push(todo);
            // 3.2 更新状态
            this.setState({
                todos: tempTodos
            })
        };
        // 4. 删除已经完成的所有任务
        delCheckedTodo = () => {
            // 4.1 取出对象
            let tempTodos = this.state.todos;
            let tempArr = [];
            tempTodos.forEach((todo, index) => {
                if (!todo.finished) {
                    tempArr.push(todo);
                }
            });
            // 4.2 更新状态
            this.setState({
                todos: tempArr,
                finishedCount: 0
            })
    
        };
        // 5. 选中/取消所有
        dealSelectedAllTodo = (flag)=>{
            // 5.1 遍历
            const tempTodos = this.state.todos;
            let finishedCount = 0;
            tempTodos.forEach((todo, index) => {
                todo.finished = flag;
            });
    
            // 5.2 处理选中的
            tempTodos.forEach((todo, index) => {
                if (todo.finished) {
                    finishedCount += 1;
                }
            });
    
            // 5.3 更新状态
            this.setState({
                todos: tempTodos,
                finishedCount
            })
    
        };
    
        render() {
            const {todos, finishedCount} = this.state;
            return (
                <div className="todo-container">
                    <div className="todo-wrap">
                        {/*头部*/}
                        <Head
                            lastTodoId={todos.length === 0 ? 0 : todos[todos.length - 1].id}
                            addOneTodo={this.addOneTodo}
                        />
                        {/*列表*/}
                        <List
                            todos={todos}
                            removeTodoWithId={this.removeTodoWithId}
                            changeTodoFinished={this.changeTodoFinished}
                        />
                        {/*尾部*/}
                        <Foot
                            finishedCount={finishedCount}
                            totalCount={todos.length}
                            delCheckedTodo={this.delCheckedTodo}
                            dealSelectedAllTodo={this.dealSelectedAllTodo}
                        />
                    </div>
                </div>
            );
        }
    }
    
    export default App;
    
    

    index.css

    body {background: rgb(248, 244, 244);}
    
    .btn {
      display: inline-block;
      padding: 8px 10px;
      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-warning {
      color: #fff;
      background-color: orange;
      border: none;
    }
    
    .btn-warning:hover {
      color: #fff;
      background-color: red;
    }
    
    .btn:focus {
      outline: none;
    }
    
    
    /*app*/
    .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;
      outline: none;
    }
    
    .todo-header input:focus {
      outline: none;
      border-color: rgba(255, 0, 0, 0.8);
      box-shadow: inset 0 1px 1px rgba(255, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6);
    }
    
    /*main*/
    .todo-main {
      margin-left: 0;
      border: 1px solid #ddd;
      border-radius: 2px;
      padding: 0;
    }
    
    .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;
      align-items: center;
      cursor: pointer;
    }
    
    li label {
      cursor: pointer;
    }
    
    
    li button.btn {
      display: none;
      margin-top: 3px;
      padding: 3px 10px;
    }
    
    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;
    }
    
    .todo-footer button {
      float: right;
      margin-top: 5px;
    }
    
    

    Foot.jsx

    import React, {Component} from 'react';
    import PropTypes from 'prop-types';
    
    export default class Foot extends Component{
        static propTypes = {
            finishedCount: PropTypes.number.isRequired, // 已经完成的任务数量
            totalCount: PropTypes.number.isRequired,   // 总任务数量
            delCheckedTodo: PropTypes.func.isRequired, // 删除已经完成的所有任务
            dealSelectedAllTodo: PropTypes.func.isRequired // 选中/取消所有
        };
        render() {
            const {finishedCount, totalCount, delCheckedTodo, dealSelectedAllTodo} = this.props;
            return (
                <div className="todo-footer">
                    <label>
                        <input
                            type="checkbox"
                            checked={finishedCount === totalCount}
                            onChange={()=>dealSelectedAllTodo(finishedCount !== totalCount)}
                        />
                    </label>
                    <span><span>已完成{finishedCount}件</span> / 总计{totalCount}件</span>
                    <button
                        className="btn btn-warning"
                        onClick={()=>delCheckedTodo()}
                    >
                        清除已完成任务
                    </button>
                </div>
            )
        }
    }
    

    Head.jsx

    import React, {Component} from 'react';
    import PropTypes from 'prop-types';
    
    export default class Head extends Component{
        constructor(props){
            super(props);
            // 绑定ref
            this.myInput = React.createRef();
        }
    
        static propTypes = {
            lastTodoId: PropTypes.number.isRequired, // 最后一条记录的ID
            addOneTodo: PropTypes.func.isRequired   // 添加一条记录
        };
    
        render() {
            return (
                <div className="todo-header">
                    <input
                        ref={this.myInput}
                        type="text"
                        placeholder="请输入今天的任务清单,按回车键确认"
                        onKeyDown={(e)=>this._handleEvent(e)}
                    />
                </div>
            )
        }
    
        _handleEvent(e){
            const {lastTodoId, addOneTodo} = this.props;
            // 1. 判断是否是回车键
            if(13 === e.keyCode){
                // 2. 判断输入的内容是否为空
                if(!this.myInput.current.value){
                    alert('输入的内容不能为空!');
                    return;
                }
                // 3. 创建todo对象返回
                const todo =  {id: lastTodoId + 1, title: this.myInput.current.value, finished: false};
                addOneTodo(todo);
                // 4. 清空内容
                this.myInput.current.value = '';
            }
        }
    }
    

    Item.jsx

    import React, {Component} from 'react';
    import PropTypes from 'prop-types';
    
    export default class Item extends Component{
        constructor(props){
            super(props);
            this.state = {
                showDelBtn: false
            }
        }
        static propTypes = {
            todo: PropTypes.object.isRequired, // 单条数据
            removeTodoWithId: PropTypes.func.isRequired, // 删除一条记录
            changeTodoFinished: PropTypes.func.isRequired, // 删除一条记录
        };
        render() {
            const {todo, removeTodoWithId, changeTodoFinished} = this.props;
            const {showDelBtn} = this.state;
            return (
                <li
                   onMouseOver={()=>this._hasShowBtn(true)}
                   onMouseOut={()=>this._hasShowBtn(false)}
                >
                    <label>
                        <input type="checkbox" checked={todo.finished} onChange={()=>changeTodoFinished(todo.id, !todo.finished)}/>
                        <span>{todo.title}</span>
                    </label>
                    <button
                        className="btn btn-warning"
                        style={{display: showDelBtn ? 'block' : 'none'}}
                        onClick={()=>removeTodoWithId(todo.id)}
                    >
                        删除
                    </button>
                </li>
            )
        }
    
        // 处理按钮的显示和隐藏
        _hasShowBtn(flag){
            this.setState({
                showDelBtn: flag
            })
        }
    }
    

    List.jsx

    import React, {Component} from 'react';
    import PropTypes from 'prop-types';
    import Item from './Item';
    
    export default class List extends Component {
        static propTypes = {
            todos: PropTypes.array.isRequired, // 数据数组
            removeTodoWithId: PropTypes.func.isRequired, // 删除一条记录
            changeTodoFinished: PropTypes.func.isRequired, // 删除一条记录
        };
    
        render() {
            const {todos, removeTodoWithId, changeTodoFinished} = this.props;
            return (
                <ul className="todo-main">
                    {
                        todos.map((todo, index) => (
                            <Item
                                key={index}
                                todo={todo}
                                removeTodoWithId={removeTodoWithId}
                                changeTodoFinished={changeTodoFinished}
                            />
                        ))
                    }
                </ul>
            )
        }
    }
    
    工程篇.png

    相关文章

      网友评论

          本文标题:bunny笔记|React-工程篇(实现todoList)

          本文链接:https://www.haomeiwen.com/subject/yiarsrtx.html