React demo

69 阅读2分钟

App.jsx

import React, { useState } from "react";
import "../styles.css";

// 引入其他组件
import NewTodoForm from "./NewTodoForm";
import TodoList from "./TodoList";

// 抽离添加任务表单
export default function App() {
  // useState 用于在函数组件中添加状态
  // 它返回一个数组,其中包含当前状态的值和一个用于更新状态的函数
  // 通常,数组的第一个元素是当前状态的值,第二个元素是更新该状态的函数
  // 语法:const [state, setState] = useState(初始值或初始化回调)
  // 这种语法叫作解构,是一个JavaScript概念,可以搜索一下什么叫解构
  const [todos, setTodos] = useState([]);

  // 这个方法用来添加Todo
  // 虽然声明在父组件中,但其实是在子组件NewTodoForm中被调用的
  // 因为调用的时候传入了title,所以好像我们从NewTodoForm中获得了title一样
  function addTodos(title) {
    setTodos((current) => {
      return [
        ...current,
        // { id: crypto.randomUUID(), title: title, completed: false },
        { id: crypto.randomUUID(), title, completed: false },
      ];
    });
  }

  // 用来更新给定id的Todo的完成状态为completed
  function toggleTodo(id, completed) {
    // 用setState的回调形式来更新,current是当前todos的值,记住它可是个数组
    setTodos((current) =>
      // 所以我们先对这个数组来进行遍历,每个todo是current中的每一项
      current.map((todo) => {
        // 如果遍历的过程中找到了我们所选的那个id,就更新它
        // 也就是返回更新后的值,...是传播运算符可以搜索一下什么是传播运算符
        if (todo.id === id) return { ...todo, completed };

        // 不是我们选的那个todo就不管它
        return todo;
      })
    );
  }

  // 删除任务
  function deleteTodo(id) {
    setTodos((current) => {
      return current.filter((todo) => {
        return todo.id !== id;
      });
    });
  }

  return (
    // 这些内容叫JSX,是一种很类似html的东西但不是html
    // 一种简单的理解是你把它理解成其中可以直接使用JavaScript的html
    // 语法也和html有许多不一样的地方,组件允许我们自定义,每个组件都是一个function
    // 可以手动搜索一下什么是JSX
    // 另外所有JSX在返回的时候都要保证只有一个根,这里我使用<></>来确保
    // <></>其实是<React.Fragment></React.Fragment>的简写
    <>
      <NewTodoForm addTodos={addTodos} />
      <h1 className="header">Todo List</h1>
      <TodoList todos={todos} toggleTodo={toggleTodo} deleteTodo={deleteTodo} />
    </>
  );
}

NewTodoForm.jsx

import { useState } from "react";

export default function NewTodoForm({ addTodos }) {
  const [newItem, setNewItem] = useState("");

  // 添加任务
  function handleSubmit(e) {
    e.preventDefault();

    console.log(e.target.value);
    

    if (newItem === "") {
      return;
    }
    addTodos(newItem);
    setNewItem("");
  }

  return (
    <form className="new-item-form" onSubmit={handleSubmit}>
      <div className="form-row">
        <label htmlFor="item">newItem</label>
        <input
          type="text"
          id="item"
          value={newItem}
          onChange={(e) => setNewItem(e.target.value)}
        />
      </div>

      <button className="btn" type="submit">
        add task
      </button>
    </form>
  );
}

TodoList.jsx

import TodoItem from "./TodoItem";

export default function TodoList({ todos, toggleTodo, deleteTodo }) {
  return (
    <>
      {todos.length === 0 && <div>No Todos</div>}

      {/* 展示任务清单 */}
      <ul className="list">
        {todos.map((todo) => (
          // 因为传入的props是一个对象(键值对),同名又可以合并键值
          // 所以{...todo}就等同于将整个todo对象作为props对象传入
          // 在TodoItem中就可以顺理成章地解构出todo的所有字段

          // <TodoItem
          //   id={todo.id}
          //   title={todo.title}
          //   completed={todo.completed}
          //   key={todo.id}
          //   toggleTodo={toggleTodo}
          //   deleteTodo={deleteTodo}
          // />

          // 键和值一样,可以简写
          <TodoItem
            {...todo}
            key={todo.id}
            toggleTodo={toggleTodo}
            deleteTodo={deleteTodo}
          />
        ))}
      </ul>
    </>
  );
}

TodoItem.jsx

import React from "react";

// 结构todo的所有字段,传入是用{...todo}的形式
export default function TodoItem(id, title, completed, toggleTodo, deleteTodo) {
  return (
    <li>
      <label>
        <input
          type="checkbox"
          checked={completed}
          onChange={(e) => toggleTodo(id, e.target.checked)}
        />
        {title}
      </label>
      <button className="btn btn-danger" onClick={() => deleteTodo(id)}>
        delete task
      </button>
    </li>
  );
}

Q1:

5.png

Q2:

6.png