likes
comments
collection
share

React + Todos:打造个性化任务管理器的实践指南

作者站长头像
站长
· 阅读数 18

前言

想必大家应该都用vue或者html写过todos,那么有尝试过用react来写todos吗?

react划分组件树,用组件的思想,其核心理念是“组件化”,组件开发、交流、互动、汇报的基本单位,DOM 树太微观了,组件树很好表达页面的构成和功能,vue和react都是根组件接管一切,那么vue和react有什么区别呢?

  • vue 有各种指令
  • react 不支持双向绑定
    • 表单受控组件 this.state.inputText控制
    • 单向的数据绑定 value = {}结合onChange 事件实现数据绑定,无v-model

正文

实现效果:

React + Todos:打造个性化任务管理器的实践指南

准备工作:

  1. 在终端分别输入:
  • npm init vite :新建react项目
  • npm i :安装包
  1. 新建一个文件夹components,再新建下面的子文件:
  • TodoForm.jsx
  • TodosList.jsx
  • TodoItem.jsx
  • TodoItem.css

index.css

清除默认的内外边距,这里我没有用*,是因为性能不太好,那么下面这种方式就可以适当的性能优化:

  • 性能优化
    • 页面的加载速度
      • 生成DOM树
      • 渲染CSSOM树
      • script 放到底部去
      • DOM + CSSOM = 渲染树
      • 布局树 (+盒子模型+bfc+zIndex)
      • GPU 去绘制页面了 像素 静态页面
    • CSS 选择器: *标签 , 从右到左匹配的
/* 性能不太好 */
body, div, dl, dt, dd, ul, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, button, textarea, blockquote{
  margin: 0;
  padding: 0;
}
/* CSS选择器的匹配规则是从右到左  少用标签*/

App.jsx

大概的思路分析:

  • 导入了 Component 类:这是创建 React 组件的基础,所有的类组件都继承自这个类
  • 导入了两个自定义的 React 组件:TodosFormTodosList
  • App 类继承了 Component 类:
  1. constructor 方法初始化组件状态: 从 localStorage 中恢复的待办事项列表:如果 localStorage 中没有找到待办事项,则默认为一个空数组
  • 定义了几个方法,分别为:
  1. addTodo 方法

用于向待办事项列表中添加一个新的待办事项,接收一个 text 参数,指的是待办事项,并将其添加到状态中的 todos 数组中

  1. deleteTodo 方法

用于删除特定索引位置的待办事项。为了不直接修改状态数组,这里首先创建了状态的一个副本,然后使用 splice 方法移除指定项,并更新状态

  1. toggleTodo 方法

用于切换待办事项的完成状态。同样,先复制状态数组,然后修改对应索引处的 completed 属性

  1. editTodo 方法

用于编辑待办事项的描述。与前两个方法类似,它也通过复制状态数组来避免直接修改状态

  • 通过一个生命周期方法componentDidUpdate 来在组件更新后被调用,每当组件状态改变并重新渲染后,都会将最新的待办事项列表存储到 localStorage 中,等到下次加载时恢复
  • 最后是 render 方法:返回要渲染到 DOM 中的 JSX:
  1. 返回一个包含标题和两个子组件 (TodosFormTodosList) 的 <div> 元素
  2. 子组件接收了来自 App 组件的方法作为属性(props),以便能够与状态进行交互
import { Component } from 'react';
import TodosForm from './components/TodosForm';
import TodosList from './components/TodosList';

// es6 模块化 es6 module
// class extends super static 传统面向对象的能力
class App extends Component{
  constructor(props) {
    super(props); // 将父类的构造函数执行一下
    // 私有数据 声明自己的属性
    const savedTodos = JSON.parse(localStorage.getItem('todos')) || [];
    this.state = {
      todos: savedTodos
    }
  }

  // 修改状态 数据流
  addTodo = (text) => {
    // Component 上有setState方法,修改状态,响应式更新
    // 状态就如纸巾
    this.setState({
      todos: [
        ...this.state.todos,
        {
          text,
          completed: false
        }
      ]
    })
  }

  // 数据编程
  // 数据和界面状态是一一对应的
  // 删除
  deleteTodo = (index) => {
     const newTodos = [...this.state.todos];
     newTodos.splice(index, 1); // 删除某一项,修改原数组
     this.setState({
       todos: newTodos
     })
  }

  // 切换
  toogleTodo = (index) => {
    const newTodos = [...this.state.todos];
    newTodos[index].completed = !newTodos[index].completed;
    this.setState({
      todos: newTodos
    })
  }

  // 修改
  editTodo = (index, newText) => {
    const newTodos = [...this.state.todos];
    newTodos[index].text = newText;
    this.setState({
      todos: newTodos
    })
  }

  componentDidUpdate() {
    localStorage.setItem('todos', JSON.stringify(this.state.todos));
  }
  // 抽象方法 abstract function
  // 界面不一样的,重写

  render(){
    return (
      <div>
        <h1>Hello React</h1>
        <div>
          <TodosForm addTodo={this.addTodo} />
          <TodosList 
          todos = {this.state.todos}
          deleteTodo={this.deleteTodo}
          toogleTodo={this.toogleTodo}
          editTodo={this.editTodo}
          />
        </div>
      </div>
    )
  }
}

export default App;

TodoForm.jsx

提供一个表单让用户输入新的待办事项,并在提交时将这些待办事项进行渲染。

大概的思路:

  • 导入了 Component
  • TodosForm 类继承了 Component 类,在构造函数 constructor 中:
  1. super(props) 被调用用来初始化父类的构造函数,确保 this.propsthis.state 正确绑定
  2. 初始化状态 state 包含一个键 inputText,其值被设置为 '聚会',为输入框的初始值
  • 定义了两个函数(事件处理器):
  1. handleChange 函数:

用于处理输入框的值变化,当用户在输入框中输入文本时,它会被触发,更新 inputText 的状态值为当前输入框的值

  1. handleSubmit 函数:

处理表单的提交事件

  • 通过e.preventDefault() 阻止表单的默认提交行为(例如页面刷新),确保应用逻辑可以正确执行

  • 如果输入框中的文本不为空(经过 trim() 去除前后空白字符后),则调用从父组件传递过来的 addTodo 方法,将新的待办事项添加到列表中

  • inputText 的状态值重置为空字符串,清空输入框

  • render方法:

  1. 返回了 JSX
  2. 定义表单的结构为:
  • 一个文本输入框:值被设置为 this.state.inputText 的值,确保输入框与状态同步,当输入框内容变化时,onChange 事件会触发 handleChange 方法
  • 一个提交按钮:当点击提交按钮或按下回车键时,onSubmit 事件会触发 handleSubmit 方法,处理表单的提交
import { Component } from 'react';
import './TodosForm.css'

class TodosForm extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputText: '聚会'
        }
    }

    handleChange = (e) => {
        this.setState({
            inputText: e.target.value
        })
    }

    handleSubmit = (e) => {
        e.preventDefault();
        
        if (this.state.inputText.trim()) {
            this.props.addTodo(this.state.inputText);
            this.setState({
                inputText: ''
            })
        }
    }
  render() {
    return (
      <form className='todo-form' onSubmit={this.handleSubmit}> 
        <input 
        type="text" 
        placeholder="请输入待办事项"
        className='todo-form__input' 
        value={this.state.inputText}
        onChange={this.handleChange}/>
    
      <button className="todo-form__button" type='submit'>add</button>  
      </form>
    );
  }
}

export default TodosForm;

TodosList.jsx

用于渲染待办事项列表

大概的思路:

  • 导入 Component 类,用于创建类组件
  • 导入了 TodoItem 组件:用来渲染单个待办事项的子组件
  • 继承了 Component 类,在 render 方法内,使用解构赋值从 this.props 中提取出 todosdeleteTodotoggleTodoeditTodo 函数(这些函数是从父组件传入的,用于处理待办事项的删除、切换完成状态和编辑)
  • render 方法:
  1. 返回 JSX
  2. 定义待办事项列表的结构:
  • <h1> 标签,用于显示标题 "Todo List"

  • 使用 .map() 方法遍历 todos 数组中的每一个待办事项

  • 对于数组中的每一个元素,创建一个 TodoItem 组件实例,将当前待办事项的数据和相关处理函数作为 props 传递给它

  • 这里的key 属性设置为当前元素的 index,有助于 React 在更新列表时更高效地识别和重用组件

  • TodosList 组件导出为默认模块,使其可以在其他文件中被导入和使用

import { Component } from 'react';
import TodoItem from './TodoItem';
import './TodosList.css';

class TodosList extends Component {
  render() {
    const { todos, deleteTodo, toogleTodo, editTodo, index } = this.props;
    return (
      <div>
        <h1>Todo List</h1>
        {
          todos.map((todo, index) => {
            return <TodoItem 
            key={index} 
            index={index} 
            todo={todo} 
            deleteTodo={deleteTodo} 
            toggleTodo={toogleTodo} 
            editTodo={editTodo}
            />;
          })
        }
       
      </div>
    );
  }
}

export default TodosList;

TodoItem.jsx

用于渲染和管理单个待办事项的显示和交互

大概的思路:

  • 导入 Component 类,用于创建类组件
  • TodoItem 类继承了 Component 类,在构造函数 constructor 中,初始化组件的本地状态 state:状态包含两个键:
  1. isEditing 表示是否处于编辑模式,初始值为 false
  2. editText 存储编辑时的文本内容,初始值为从父组件传入的待办事项的文本
  • 定义了两个函数:
  1. handleEditChange 函数:

处理编辑模式下输入框内容的变化,更新 editText 的状态值

  1. handleEditSave 函数:

处理编辑模式下的保存操作

  • 调用从父组件传入的 editTodo 函数

  • 传递当前待办事项的索引和编辑后的文本

  • 更新全���的待办事项列表

  • isEditing 状态设为 false

  • render 方法:

  1. 使用解构赋值从 this.props 中提取出 todo 对象以及处理函数 deleteTodotoggleTodoeditTodo
  2. todo 对象中提取出 textcompleted 属性
  3. 返回 JSX
  4. 定义待办事项的布局:
  • 包裹在一个 <li> 标签中
  • 其类名根据待办事项的完成状态动态变化,以应用不同的样式
  • 根据 isEditing 状态的值,组件会呈现两种不同的视图:
  1. 如果 isEditing 为 true,则显示一个输入框和一个保存按钮,允许用户编辑待办事项的文本
  2. 如果 isEditing 为 false,则显示待办事项的文本、一个编辑按钮和一个删除按钮。文本被包装在一个 <span> 标签中,点击时会调用 toggleTodo 函数,切换待办事项的完成状态
import { Component } from 'react';
import './TodoItem.css';

class TodoItem extends Component {
    constructor(props) {
        super(props)

        this.state = {
            isEditing: false,
            editText:this.props.todo.text
        }
    }

   handleEditChange = (event) => {
    this.setState({
        editText: event.target.value
    })
   }

   handleEditSave = () => {
    this.props.editTodo(this.props.index, this.state.editText)
    this.setState({
        isEditing: false
    })
   }
  render () {
    const { todo, deleteTodo, toggleTodo, editTodo, index } = this.props
    console.log(todo);
    const { text, completed } = todo
    return (
      <li className={`todo-item ${completed ? 'todo-item--completed' : ''}`}>
        {
            this.state.isEditing?(
                <div>
                    <input 
                        type="text"
                        value={this.state.editText} 
                        onChange={this.handleEditChange}
                    />  
                    <button onClick={this.handleEditSave}>保存</button>
                </div>
            ):(
            <div>
                <span className="todo-item__text"
                onClick={() => toggleTodo(index)}>
                {todo.text}
                </span>
                <button onClick={() => this.setState({isEditing: true})}>编辑</button>
                <button className="todo-item__delete-btn" 
                onClick={() => deleteTodo(index)}>Delete</button>
            </div>
            )
        }
        
      </li>
    );
  }
}

export default TodoItem;

TodoItem.css

用于实现当点击待办事项时,实现待办事项文本的样式变化

.todo-item--completed .todo-item__text{
    text-decoration: line-through;
    color: #888;
}

结语

React + Todos:打造个性化任务管理器的实践指南

转载自:https://juejin.cn/post/7387999151412232227
评论
请登录