实现一个TodoList---React

效果图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设计步骤:

  1. 拆分组件 拆分界面 抽离组件
  2. 实现静态页面,不用实现仍的交互
  3. 实现动态页面
    a. 动态显示初始化数据
    ⅰ. 数据类型
    ⅱ. 数据名称
    ⅲ. 保存在哪一个组件
    b. 交互(从绑定事件开始)
    首先,将TodoList拆分成三个组件:
    在这里插入图片描述

组件目录:

在这里插入图片描述
首先实现的是:静态页面,就jsx和css配合,不用涉及变量,画页面
然后,绑定事件,涉及父子组件传值、遍历的时候数据结构设计。
搜索框组件:Search

Search.jsx
import { nanoid } from 'nanoid';
import "./index.css";

export default function Search(props) {
  const handleInput = (e) => {
    if (e.keyCode === 13) {
      // 将获取到的事件传递给Tasks
      props.addTask({id:nanoid(),isFinish:false,text:e.target.value});
      e.target.value = '';
    }
  }
  return <input  type="text" onKeyDown={handleInput} placeholder="请输入你的任务名称,按回车键确认"/>;
}

Search.css
input[type="text"] {
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 100%;
  margin: 10px auto;
  padding: 5px 0;
}
input[type="text"]:focus {
  border: 1px solid #ccc;
  border-radius: 4px;
}

Tasks.jsx
import {Component} from 'react';
import Task from './Task/Task';
import Search from '../Search/Search';
import { nanoid } from 'nanoid';
import './Tasks.css';


export default class Tasks extends Component { 
  constructor(props) {
    super(props);
    this.state = {
      tasks: [{id: nanoid(),isFinish:true,text:'吃饭'},{id: nanoid(),isFinish: false,text:'睡觉'}],
      flag: false
    }
  }
  handleDeleteTask = (e,task) => {
    let {tasks,flag} =this.state;
    // 弹出提示框 是否删除
    const historyTasks = tasks.slice();
    var remainTasks = [];
    if (e.target.id === 'dall') {
      remainTasks = historyTasks.filter(item => item.isFinish !== true);
      flag = flag === true && !flag;
    } else {
      remainTasks = historyTasks.filter(item => item.id !== task.id);
    }
    this.setState({tasks: remainTasks,flag});
  }
  handleChange = (e,task) => {
    let {tasks,flag} = this.state;
    const historyTasks = tasks.slice();
    var dealTask = [];
    if (e.target.id === 'all') {
      dealTask = historyTasks.map(item => {
        let {id,text} = item;
        return flag === true 
          ? {id,isFinish:false,text}
          : {id,isFinish: true, text};
      })
    } else{
      dealTask = historyTasks.map(item => {
        let {id,isFinish,text} = item;
        if (item.id === task.id) {
          item = {id,isFinish:!isFinish,text};
        }
        return item;
      })
    }
    let {length} = dealTask.filter(task => task.isFinish === true);
    flag = flag !==0 && length === historyTasks.length;
    this.setState({tasks:dealTask,flag});
  }
  handleAddTask = (task) => {
    let tasks = [task, ...this.state.tasks];
    this.setState({tasks: tasks});
  }
  render() {
    const {tasks,flag} = this.state;
    let {length} = tasks.filter(task => task.isFinish === true);
    return (
      <div>
        <Search addTask={this.handleAddTask}/>
        <Task tasks={this.state.tasks} handleChange={this.handleChange} handleDeleteTask={this.handleDeleteTask}/>
        <div className="task_item">
            <div>
              <input type="checkbox" id="all" onChange={this.handleChange} checked={flag === true}/>
              <span>已完成{length}/全部{this.state.tasks.length}</span>
            </div>
            <button onClick={this.handleDeleteTask} id="dall">删除所有已完成任务</button>
          </div>
      </div>
    )
  }
}
Tasks.css
.tasks_container {
  border: 1px solid #ccc;
}
.tasks_container .task_item:not(:last-child){
  border-bottom: 1px solid #ccc;
}

Task.jsx
import {Component} from 'react';
import "./Task.css";

export default class Task extends Component {
  handleChange = (task) => {
    return (e) => {
      this.props.handleChange(e,task);
    }
  }
  handleDeleteTask = (task) => {
    return (e) => {
      this.props.handleDeleteTask(e,task);
    }
  }
  render() {
    let tasks = this.props.tasks;
    return (
      <div>
        {tasks.map(task=> {
          return <div className="task_item" key={task.id}>
            <div>
              <input type="checkbox" id="task" onChange={this.handleChange(task)} checked={task.isFinish === true}/>
              <span>{task.text}</span>
            </div>
            <button onClick={this.handleDeleteTask(task)}>删除</button>
          </div>
        })}
      </div>
    );
  }
}
Task.css
.task_item{
  display:flex;
  padding:5px 0;
  color:#333333;
  width:100%;
}
.task_item>div{
  flex:1;
}
.task_item>button{
  border:none;
  background-color:red;
  border-radius: 4px;
  color:#eee;
  display: none;
}
.task_item:hover{
  background-color:#ccc;
}
.task_item:hover button{
  display:inline-block;
}
App.js
import Tasks from "./components/Tasks/Tasks";

import "./styles.css";

export default function App(){
    return (
      <div className="App_container">
        <Tasks/>
      </div>
    );
}
styles.css
.App_container {
  font-size: 16px;
  border: 1px solid #ccc;
  box-shadow: 1px 1px 2px  2px #ccc;
  padding: 10px;
  width: 60%;
}

index.js
import React, { StrictMode } from "react";
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <StrictMode>
    <App />
  </StrictMode>,
  rootElement
);

遇到过的问题:
  1. Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    翻译:
    超过最大更新深度。当组件在componentWillUpdate或componentDidUpdate内部反复调用setState时,就会发生这种情况。React限制嵌套更新的数量,以防止无限循环。
  2. 子组件是不能更改props传递的内容的,需要在父组件中更新才行
  3. 划分组件的时候,要考虑数据结构,方便更新,减少嵌套层数。
  4. 绑定事件处理函数的时候,使用回调传递参数
handleChange = (task) => {
    return (e) => {
      this.props.handleChange(e,task);
    }
  }
  1. 遍历时,引入nanoid依赖,生成唯一id,作为key

备注:该项目是B站的资源,但是自己先搞一遍,然后再去看视频,看看思路差异。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值