【Typescript学习】使用 React 和 TypeScript 构建web应用(三)所有组件

news2024/10/6 2:29:58

教程来自freecodeCamp:【英字】使用 React 和 TypeScript 构建应用程序
跟做,仅记录用
其他资料:https://www.freecodecamp.org/chinese/news/learn-typescript-beginners-guide/


第三天

以下是视频(0:40-0:60) 的内容

目录

  • 第三天
    • 1 创建TodoList组件
    • 2 SingleTodo组件
    • 3 todo的按钮响应函数

1 创建TodoList组件

就是装载所有todo卡片container
在这里插入图片描述

App中以prop传入todos数组、setTodos方法
App.tsx

return (
    <div className="App">
      <span className='heading'>Taskify</span>
      <InputField todo={todo} setTodo={setTodo} handleAdd={handleAdd}/>
      <TodoList todos={todos} setTodos={setTodos}/>  
    </div>
  );

TodoList 中的每一个卡片是SingleTodo组件(在后文),TodoList组件的结构和UI如下
TodoList.tsx

import React from 'react'
import './styles.css'
import { Todo } from "../model";
import SingleTodo from './SingleTodo';
type Props = {
    todos: Todo[],
    setTodos: React.Dispatch<React.SetStateAction<Todo[]>>,
}

const TodoList:React.FC<Props> = ({todos, setTodos}: Props) => {
  return (
    <div className='todos'>
        {todos.map(todo =>(
            <SingleTodo key={todo.id}
                todo={todo}  
                todos={todos}
                setTodos={setTodos}/>
        ))}
    </div>
  )
}

export default TodoList

styles.css

// 之前的省略...
.todos {
    display: flex;
    justify-content: space-evenly;
    width: 90%;
    flex-wrap: wrap;  /* 元素可以换行 */
}

2 SingleTodo组件

Todo列表中的每个Todo卡片,包括tood描述、编辑按钮、删除按钮和完成按钮
在这里插入图片描述
icons图标我们可以使用react的UI组件react-icons
在这里插入图片描述

安装

npm install react-icons

在网页的搜索栏可以搜到按钮的名字
在这里插入图片描述
在这里插入图片描述
使用

import React from 'react'
import { Todo } from "../model";
import { AiFillEdit, AiFillDelete } from "react-icons/ai";
import { MdDone } from "react-icons/md";
import './styles.css'
type Props = {
    todo: Todo,
    todos: Todo[],
    setTodos: React.Dispatch<React.SetStateAction<Todo[]>>,
}


const SingleTodo = ({todo, todos, setTodos}: Props) => {
  return (
    <form className='todos__single'>
        <span className="todos__single--text">{todo.todo}</span>
        <div>
            <span className="icon"><AiFillEdit/></span>
            <span className="icon"><AiFillDelete/></span>
            <span className="icon"><MdDone/></span>
        </div>
    </form>
  )
}

(SingleTodo的HTML标签是form,因为当修改todo时需要处理submit,后文会实现)
css样式设计
styles.css

// 之前的省略...
.todos__single {
    display: flex;
    width: 29.5%;
    border-radius: 5px;
    padding: 20px;
    margin-top: 15px;
    background-image: url("https://img.freepik.com/free-photo/crumpled-yellow-paper-background-close-up_60487-2390.jpg?size=626&ext=jpg");
}
.todos__single--text {
    flex: 1;
    padding: 5px;
    border: none;
    font-size: 20px;
}
.todos__single--textJ:focus {
    outline: none;
}

.icon {
    margin-left: 10px;
    font-size: 25px;
    cursor: pointer;
}

/* 响应式布局 */
@media (max-width: 1200px) {
    .todos__single {
        width: 40%;
    }
}
@media (max-width: 700px) {
    .input {
        width: 95%;
    }
    .todos {
        width: 95%;
    }
    .todos__single {
        width: 100%;
    }
}

其中响应式布局的效果如下,当宽度小于700px时:
在这里插入图片描述

3 todo的按钮响应函数

分别对三个按钮实现功能,较简单的是点击完成(done)的事件响应和删除(delete)的事件响应
SingleTodo.tsx

// done按钮的响应
  const handleDone = (id: number) => {
    // 改变对应todo的isDone属性
    setTodos(
        todos.map((todo) => 
            todo.id === id ? {...todo, isDone: !todo.isDone}
                           : todo
        )
    );
  };
  // delete按钮的响应
  const handleDelete = (id: number ) => {
    setTodos(
        todos.filter((todo) => todo.id === id)
    )
  };
  return (
    <form className='todos__single'>
        {// 如果是完成状态,则用删除线效果
          todo.isDone ? (
           // <s> 删除线标签
            <s className="todos__single--text">{todo.todo}</s>
        ):(
            <span className="todos__single--text">{todo.todo}</span>
        )}
        <div>
            <span className="icon"><AiFillEdit/></span>
            <span className="icon" onClick={()=>handleDelete(todo.id)}><AiFillDelete/></span>
            <span className="icon" onClick={()=>handleDone(todo.id)}><MdDone/></span>
        </div>
    </form>
  )

点击edit按钮的事件响应逻辑比较复杂

  1. 我们需要一个状态变量edit,跟踪当前是否处于“编辑状态”;以及一个状态变量editTodo,跟踪当前输入到todo的文本。
const [edit, setEdit] = useState<boolean>(false);
const [editTodo, setEditTodo] = useState<string>(todo.todo);
  1. 在结构里添加显示的逻辑和编辑完成后提交时的事件
<form className='todos__single' onSubmit={(e)=>handleEdit(e, todo.id)}>
    {   // 如果是edit状态,则显示input框,否则是todo的描述
        edit ? (
            <input value={editTodo} 
               onChange={(e)=> setEditTodo(e.target.value)}
               className='todos__single--text'
            />
        ) : (
            // 如果是完成状态,则用删除线效果
            todo.isDone ? (
                // s 下划线标签
                <s className="todos__single--text">{todo.todo}</s>
            ):(
                <span className="todos__single--text">{todo.todo}</span>
            )
        )
    }
    ...
</form>
  1. 处理handleEdit函数
const handleEdit = (e:React.FormEvent, id: number) => {
  e.preventDefault();  // 禁止默认的页面刷新行为
  setTodos(todos.map((todo => (
      todo.id === id? {...todo, todo: editTodo}:todo
      )
  )))
  setEdit(false);
}
  1. 我们需要点击edit按钮时,input框自动获得焦点,因此用useRef挂载到input框上,同时使用对edit变量的useEffect
  const inputRef = useRef<HTMLInputElement>(null);
  useEffect(() => {
    inputRef.current?.focus();
  }, [edit]);

第三天done!
今天新建了TodoList组件和SingleTodo组件,完成了相应的UI和大部分的逻辑。
UI中需要注意适应窗口的响应式布局、如何使用react-icons框架;组件实现中需要注意具体的逻辑、状态变量如何切换

TodoList.tsx

import React from 'react'
import './styles.css'
import { Todo } from "../model";
import SingleTodo from './SingleTodo';
type Props = {
    todos: Todo[],
    setTodos: React.Dispatch<React.SetStateAction<Todo[]>>,
}

const TodoList:React.FC<Props> = ({todos, setTodos}: Props) => {
  return (
    <div className='todos'>
        {todos.map(todo =>(
            <SingleTodo key={todo.id}
                todo={todo}  
                todos={todos}
                setTodos={setTodos}/>
        ))}
    </div>
  )
}

export default TodoList

SingleTodo.tsx

import React, { useState, useRef, useEffect } from 'react'
import { Todo } from "../model";
import { AiFillEdit, AiFillDelete } from "react-icons/ai";
import { MdDone } from "react-icons/md";
import './styles.css'
type Props = {
    todo: Todo,
    todos: Todo[],
    setTodos: React.Dispatch<React.SetStateAction<Todo[]>>,
}


const SingleTodo = ({todo, todos, setTodos}: Props) => {
  const [edit, setEdit] = useState<boolean>(false);
  const [editTodo, setEditTodo] = useState<string>(todo.todo);

  // done按钮的响应
  const handleDone = (id: number) => {
    // 改变对应todo的isDone属性
    setTodos(
        todos.map((todo) => 
            todo.id === id ? {...todo, isDone: !todo.isDone}
                           : todo
        )
    );
  };
  // edit按钮的响应
  const handleEdit = (e:React.FormEvent, id: number) => {
    e.preventDefault();  // 禁止默认的页面刷新行为
    setTodos(todos.map((todo => (
        todo.id === id? {...todo, todo: editTodo}:todo
        )
    )))
    setEdit(false);
  }
  // delete按钮的响应
  const handleDelete = (id: number ) => {
    setTodos(
        todos.filter((todo) => todo.id === id)
    )
  };
  
  const inputRef = useRef<HTMLInputElement>(null);  // 编辑框
  // edit状态改变时自动获取编辑框的焦点
  useEffect(() => {  
    inputRef.current?.focus();
  }, [edit]);
  
  return (
    <form className='todos__single' onSubmit={(e)=>handleEdit(e, todo.id)}>
        {   // 如果是edit状态,则显示input框,否则是todo的描述
            edit ? (
                <input
                  ref={inputRef} 
                  value={editTodo} 
                  onChange={(e)=> setEditTodo(e.target.value)}
                  className='todos__single--text'
                />
            ) : (
                // 如果是完成状态,则用删除线效果
                todo.isDone ? (
                    // s 下划线标签
                    <s className="todos__single--text">{todo.todo}</s>
                ):(
                    <span className="todos__single--text">{todo.todo}</span>
                )
            )
        }
        
        <div>
            <span className="icon" onClick={()=>{
                if(!edit && !todo.isDone){
                    setEdit(!edit);  // edit变为true
                }
            }}><AiFillEdit/></span>
            <span className="icon" onClick={()=>handleDelete(todo.id)}><AiFillDelete/></span>
            <span className="icon" onClick={()=>handleDone(todo.id)}><MdDone/></span>
        </div>
    </form>
  )
}

export default SingleTodo

明天要使用react reducer hook进行状态管理了,可以先复习一下

使用到的:

  • react的按钮UI组件react-icons
  • 响应式布局

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaEE day6 初识JavaScript

什么是JS JS是通行在各种浏览器的一种语言&#xff0c;JAVA后端代码运行在服务器上&#xff0c;JS代码内容配合HTML&#xff0c;浏览器对JS代码进行解释运行&#xff0c;然后展现在浏览器上&#xff0c;web开发离不开JS。 一般步骤为&#xff1a;&#xff08;index.html与scr…

LinuxC—高级IO

高级IO 1 非阻塞IO/有限状态机编程 1.1 基本概念 定义 有限状态机(Finite State Machine) 缩写为 FSM&#xff0c;状态机有 3 个组成部分&#xff1a;状态、事件、动作。 状态&#xff1a;所有可能存在的状态。包括当前状态和条件满足后要迁移的状态。事件&#xff1a;也称为…

自动驾驶环境感知——视觉传感器技术

文章目录1. 摄像头的成像原理1.1 单目视觉传感器的硬件结构1.2 单目视觉的成像原理 –小孔成像模型1.3 单目视觉的成像原理 – 像素坐标系1.4 单目视觉三维坐标系转换 – 外参1.5 单目视觉的坐标系转换 – 从世界坐标点到像素坐标1.6 单目视觉的特性2. 视觉传感器的标定2.1 视觉…

CSS之精灵图

1. 精灵图 1.1 为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 为什么使用精灵图&#xff…

9、断点调试

文章目录9、断点调试9.1 为什么需要Debug9.2 Debug的步骤1 添加断点2 启动调试3 单步调试工具介绍9.3 多种Debug情况介绍1 行断点2 方法断点3 字段断点4 条件断点5 异常断点6 线程断点7 强制结束9.4 自定义调试数据视图9.5 常见问题【尚硅谷】idea实战教程-讲师&#xff1a;宋红…

Linux安装mysql--CentOS系统

Linux安装mysql 安装包&#xff1a; https://pan.baidu.com/s/10xvFpfl4nTktaEdhKbY3og 首先启动虚拟机&#xff0c;我是用FinalShell连接的 然后将下载的安装包上传至Linux系统中&#xff0c;直接rz回车就会跳出选择文件的窗口&#xff0c;选择需要上传的安装包即可等待上传…

计算机网络01_---软考高级系统架构师010

计算机网络知识点汇总: IPV4中有单播,组播,广播.IPV6没有广播 网络标准喝协议中要知道有IEEE802.3 以太网协议 局域网是以太网的一种 然后,IEEE802.11是无线局域网协议. TCP/IP协议族,这里,要知道从网络层开始,这里到传输层,然后传输层有个 TCP协议,这里TCP链接的时候有…

【沐风老师】3DMAX地板生成器插件FloorGenerator使用教程

FloorGenerator地板生成器插件&#xff0c;创建任何形状的地板几何图形&#xff0c;你可以完全控制从斜边到木板倾斜的所有参数。 伴随该地板的是”多重纹理贴图&#xff08;MultiTexture&#xff09;“插件&#xff0c;它使你能够将任意数量的位图随机指定给生成的地板。还提…

听障人士亲述:我们在VRChat用手语交流,成员规模5000人

如果你在B站上搜索VRChat&#xff0c;排在前面的热门视频几乎都是与老外聊天的内容。除了练习语言、交文化流外&#xff0c;你还能在VRChat上遇到不少哇哇乱叫的小孩。作为一款VR社交应用&#xff0c;除了有趣的小游戏外&#xff0c;说话聊天也是VRChat关键的玩法之一。而有这么…

离线ctr特征中心更新

3.8 离线ctr特征中心更新 学习目标 目标 了解特征服务中心的作用应用 无 3.8.1 特征服务中心 特征服务中心可以作为离线计算用户与文章的高级特征&#xff0c;充当着重要的角色。可以为程序提供快速的特征处理与特征结果&#xff0c;而且不仅仅提供给离线使用。还可以作为实时…

【My Electronic Notes系列——直流稳压电源】

目录 序言&#xff1a; &#x1f3c6;&#x1f3c6;人生在世&#xff0c;成功并非易事&#xff0c;他需要破茧而出的决心&#xff0c;他需要永不放弃的信念&#xff0c;他需要水滴石穿的坚持&#xff0c;他需要自强不息的勇气&#xff0c;他需要无畏无惧的凛然。要想成功&…

快速安装OpenShift在Ubuntu系统上并使用

目录 OpenShift简介&#xff1a; 服务器信息 安装Docker 安装OpenShift 访问Dashboard oc常用命令 附 OpenShift简介&#xff1a; OpenShift 是一个开源容器应用平台&#xff0c;由 Red Hat 开发。它建立在 Kubernetes 之上&#xff0c;并提供用于部署、扩展和管理容器…

【Linux】基础:线程的同步与互斥

【Linux】基础&#xff1a;线程的同步与互斥 摘要&#xff1a;本文主要介绍线程的同步与互斥方面的内容&#xff0c;分为理论与实现两部分完成。首先从整体上介绍线程同步与互斥相关概念&#xff0c;在理解概念后对两者分开介绍。在互斥方面&#xff0c;主要介绍内容为互斥量的…

LinuxC—线程

线程 1 线程的基本概念 什么是线程 进程其实是一个容器&#xff0c;当我们在编程的时候实际上是在以线程为单位进行编程&#xff0c;包括处理器的调度也是以线程为单位的&#xff0c;一个进程可以有多个线程&#xff0c;一个进程的多个线程共享相同的进程空间&#xff0c;所以…

设计模式 - 创建型模式_抽象工厂模式

文章目录创建型模式概述Case场景模拟工程模拟早期单机Redis的使用Bad ImplBetter Impl &#xff08;抽象⼯⼚模式重构代码&#xff09;定义适配接⼝实现集群适配器接口代理方式的抽象工厂类单元测试小结创建型模式 创建型模式提供创建对象的机制&#xff0c; 能够提升已有代码…

0、Spring工程构建Spring快速入门Spring配置文件详解注入Sprint相关API

1、Spring工程构建 创建工程项目目录文件夹 IDEA选择项目new一个module 配置案例 aop创建 创建并下载完毕后&#xff0c;点击file选择projert 选择按照的jdk版本 output选择当前目录&#xff0c; 点击右下方apply 选择facets&#xff0c;点击""号选择web 选择当前…

Pinia状态管理

1、Pinia和Vuex的对比 1.1、什么是Pinia呢&#xff1f; Pinia&#xff08;发音为/piːnjʌ/&#xff0c;如英语中的“peenya”&#xff09;是最接近pia&#xff08;西班牙语中的菠萝&#xff09;的词&#xff1b; Pinia开始于大概2019年&#xff0c;最初是作为一个实验为Vue…

Linux使用操作

文章目录各类小技巧&#xff08;快捷键&#xff09;软件安装systemctl软连接日期、时区IP地址、主机名IP地址和主机名虚拟机配置固定IP网络传输下载和网络请求端口进程管理主机状态环境变量上传、下载压缩、解压各类小技巧&#xff08;快捷键&#xff09; 强制停止 Linux某些程…

python语法 dot函数

dot是numpy里的函数&#xff0c;主要用于求向量相乘&#xff0c;矩阵乘法&#xff0c;矩阵与向量乘法一、一维向量相乘要求元素个数相同&#xff0c;相当于求内积&#xff0c;对应元素相乘再相加&#xff0c;“1*3 2*4 11”二、矩阵和矩阵相乘遵循矩阵乘法法则“左行 * 右列”…

高通平台开发系列讲解(WIFI篇)什么是WLAN无线局域网

文章目录 一、什么是WLAN1.1、WLAN发展史1.2、WLAN工作频段二、高通相关文件2.1、配置文件2.2、开机启动2.3、wpa_supplicant沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文将基于高通平台介绍什么是无线局域网。 一、什么是WLAN 在WLAN领域被大规模推广和商用的是…