todoList_2-逻辑实现

Tutorial: React基础 Category: React Published: 2026-04-07 13:58:26 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} />
      }
    }
    
  2. 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>
        );
      }
    }
    
  3. 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>
          </>
        );
      }
    }
    
  4. 如图 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} />;
      }
    }
    
  2. 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} />
        );
      }
    }
    
  2. 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>
        );
      }
    }
    
  3. 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}
          />
        );
      }
    }
    
  2. 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}
          />
        );
      }
    }
    
  3. 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}/>
        );
      }
    }
    
  2. 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>
        );
      }
    }
    
  3. 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} />
        );
      }
    }
    
  4. 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}
        />;
      }
    }
    
  2. Footer 触发方法即可

    export default class Footer extends Component {
      // 清除已完成任务的回调
      handleClearAllDone = () => {
        this.props.clearAllDone();
      };
    
      render() {
        ...
        <button onClick={this.handleClearAllDone}>清除已完成任务</button>;
      }
    }