ThatManK Mobile Article

todoList_2-逻辑实现

Category: React Tutorial: React基础 Published: 2026-04-07 13:58 Views: 20 Likes: 0 Comments: 0

逻辑实现

一、渲染 todo 列表
  1. App.js 初始化状态, 把状态传给 List
   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 },
       ],
     };

     render() {
       const { todos } = this.state;
       ...
       <List todos={todos} />
     }
   }
  1. List 接受传参, 并传递给 Item
   import PropTypes from "prop-types";

   export default class List extends Component {
     //对接收的props进行:类型、必要性的限制
     static propTypes = {
       todos: PropTypes.array.isRequired,
     };

     render() {
       const { todos } = this.props;
       return (
         <div className="list">
           {todos.map((todo) => {
             return <Item key={todo.id} {...todo} />;
           })}
         </div>
       );
     }
   }
  1. Item 渲染数据
   export default class Item extends Component {
     render() {
       const { id, name, done } = this.props;

       return (
         <>
           <li>
             <label style={{ display: "flex" }}>
               <input
                 type="checkbox"
                 style={{ verticalAlign: "middle", marginTop: 0 }}
               />
               <span>{name}</span>
             </label>
             <button style={{ display: "none" }}>删除</button>
           </li>
         </>
       );
     }
   }
  1. 如图

react-02

二、实现添加 todo 对象
  1. App.js 将添加 todo 的方法传递给 Header, 并处理 Header 添加的 todo 对象
   export default class App extends Component {
     // addTodo用于添加一个todo, 接收的参数是todo对象
     addTodo = (todoObj) => {
       // 获取原todos
       const { todos } = this.state;
       // 追加一个todo
       const newTodos = [todoObj, ...todos];
       // 更新状态
       this.setState({ todos: newTodos });
     };

     render() {
      ...
      <Header addTodo={this.addTodo} />;
     }
   }
  1. Header 接受方法参数添加业务逻辑,并传回要添加的 todo 对象
   import PropTypes from "prop-types";
   // 生成随机ID, 需要提前 `npm add nanoid`
   import { nanoid } from "nanoid";

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

     // 键盘事件的回调
     handleKeyUp = (event) => {
       // 解构赋值获取keyCode,target
       const { keyCode, target } = event;
       // 判断是否是回车按键
       if (keyCode !== 13) return;

       // 添加的todo名字不能为空
       if (target.value.trim() === "") {
         alert("输入不能为空");
         return;
       }

       // 准备好一个todo对象
       const todoObj = { id: nanoid(), name: target.value, done: false };
       // 将todoObj传递给App
       this.props.addTodo(todoObj);
       // 清空输入
       target.value = "";
     };

     render() {
       return (
         <>
           <input
             className="header-input"
             onKeyUp={this.handleKeyUp}
             type="text"
             placeholder="请输入任务名称, 摁回车键确认"
           />
         </>
       );
     }
   }
三、实现删除 tudo 对象
  1. App.js 添加删除 todo 方法, 传递给 List, 并处理 List 回传的 id,根据 id 删除
   export default class App extends Component {
     //deleteTodo用于删除一个todo对象
     deleteTodo = (id) => {
       //获取原来的todos
       const { todos } = this.state;
       //删除指定id的todo对象
       const newTodos = todos.filter((todoObj) => {
         return todoObj.id !== id;
       });
       //更新状态
       this.setState({ todos: newTodos });
     };

     render() {
       return (
        ...
        <List todos={todos} deleteTodo={this.deleteTodo} />
       );
     }
   }
  1. List 接受传参, 并直接发送给 Item
   export default class List extends Component {
     //对接收的props进行:类型、必要性的限制
     static propTypes = {
       ...
       deleteTodo: PropTypes.func.isRequired,
     };

     render() {
       const { todos, deleteTodo } = this.props;

       return (
         <div className="list">
           {todos.map((todo) => {
             return <Item key={todo.id} {...todo} deleteTodo={deleteTodo} />;
           })}
         </div>
       );
     }
   }
  1. Item 处理业务逻辑, 并回传任务 id
   export default class Item extends Component {
     state = { mouse: false }; // 标识鼠标移入、移出

     // 鼠标移入、移出的回调
     handleMouse = (flag) => {
       return () => {
         this.setState({ mouse: flag });
       };
     };

     // 删除一个todo的回调
     handleDelete = (id) => {
       if (window.confirm("确定删除吗?")) {
         this.props.deleteTodo(id);
       }
     };

     render() {
       const { id, name, done } = this.props;
       const { mouse } = this.state;

       return (
         <>
           <li
             style={{ backgroundColor: mouse ? "#ddd" : "white" }}
             onMouseEnter={this.handleMouse(true)}
             onMouseLeave={this.handleMouse(false)}
           >
             <label style={{ display: "flex" }}>
               <input
                 type="checkbox"
                 style={{ verticalAlign: "middle", marginTop: 0 }}
               />
               <span>{name}</span>
             </label>
             <button
               style={{ display: mouse ? "block" : "none" }}
               onClick={() => this.handleDelete(id)}
             >
               删除
             </button>
           </li>
         </>
       );
     }
   }
四、实现 checkbox 更新 tudo 对象
  1. App.js 实现更新方法, 并传递给 List
    export default class App extends Component {
      // updateTodo用于更新一个todo对象
      updateTodo = (id, done) => {
        // 获取状态中的todos
        const { todos } = this.state;
        // 匹配处理数据
        const newTodos = todos.map((todoObj) => {
          if (todoObj.id === id) return { ...todoObj, done };
          else return todoObj;
        });
        this.setState({ todos: newTodos });
      };

      render() {
        const { todos } = this.state;

        return (
          ...
          <List
            todos={todos}
            deleteTodo={this.deleteTodo}
            updateTodo={this.updateTodo}
          />
        );
      }
    }
  1. List 传递给 Item 处理
    export default class List extends Component {
      static propTypes = {
        ...
        updateTodo: PropTypes.func.isRequired,
      };

      render() {
        const { todos, deleteTodo, updateTodo } = this.props;

        return (
          ...
          <Item
            key={todo.id}
            {...todo}
            deleteTodo={deleteTodo}
            updateTodo={updateTodo}
          />
        );
      }
    }
  1. Item 处理并回传 item 的 id 和选中的状态
    export default class Item extends Component {
      // 勾选、取消勾选某一个todo的回调
      handleCheck = (id) => {
        return (event) => {
          this.props.updateTodo(id, event.target.checked);
        };
      };

      render() {
        const { id, name, done } = this.props;
        const { mouse } = this.state;

        return (
          ...
          <input
            type="checkbox"
            style={{ verticalAlign: "middle", marginTop: 0 }}
            checked={done}
            onChange={this.handleCheck(id)}
          />
        );
      }
    }
五、实现 Footer 对 todo 全选
  1. App.js 将 todo 列表传给 Footer
    export default class App extends Component {

      render() {
        const { todos } = this.state;

        return (
          ...
          <Footer todos={todos}/>
        );
      }
    }
  1. Footer 实时渲染
    export default class Footer extends Component {
      render() {
        const { todos } = this.props;
        // 已完成的个数
        const doneCount = todos.reduce(
          (pre, todo) => pre + (todo.done ? 1 : 0),
          0
        );
        // 总数
        const total = todos.length;

        return (
          <div className="footer">
            <label>
              <input
                type="checkbox"
                style={{ verticalAlign: "middle", marginTop: 0 }}
                checked={doneCount === total && total !== 0 ? true : false}
              />
            </label>
            <span>
              <span>已完成{doneCount}</span> / 全部{total}
            </span>
            <button>清除已完成任务</button>
          </div>
        );
      }
    }
  1. App.js 将全部选定/取消的方法传给 Footer
    export default class App extends Component {
      // checkAllTodo用于全选
      checkAllTodo = (done) => {
        // 获取原来的todos
        const { todos } = this.state;
        // 加工数据
        const newTodos = todos.map((todoObj) => {
          return { ...todoObj, done };
        });
        // 更新状态
        this.setState({ todos: newTodos });
      };

      render() {
        ...
        return (
          ...
          <Footer todos={todos} checkAllTodo={this.checkAllTodo} />
        );
      }
    }
  1. Footer 实现逻辑, 并回传
    export default class Footer extends Component {
      // 全选checkbox的回调
      handleCheckAll = (event) => {
        this.props.checkAllTodo(event.target.checked);
      };

      render() {
        ...
        <input
          type="checkbox"
          style={{ verticalAlign: "middle", marginTop: 0 }}
          checked={doneCount === total && total !== 0 ? true : false}
          onChange={this.handleCheckAll}
        />;
      }
    }
六、实现 Footer 对 todo 选中删除
  1. App.js 删除选中的方法
    export default class App extends Component {
      // clearAllDone用于清除所有已完成的
      clearAllDone = () => {
        // 获取原来的todos
        const { todos } = this.state;
        // 过滤数据
        const newTodos = todos.filter((todoObj) => {
          return !todoObj.done;
        });
        // 更新状态
        this.setState({ todos: newTodos });
      };

      render() {
        <Footer
          todos={todos}
          checkAllTodo={this.checkAllTodo}
          clearAllDone={this.clearAllDone}
        />;
      }
    }
  1. Footer 触发方法即可
    export default class Footer extends Component {
      // 清除已完成任务的回调
      handleClearAllDone = () => {
        this.props.clearAllDone();
      };

      render() {
        ...
        <button onClick={this.handleClearAllDone}>清除已完成任务</button>;
      }
    }