首页 后端 正文

Vite+React+TypeScript手撸TodoList

布局与样式

一个TodoList长什么样子相信无需多言: Vite+React+TypeScript手撸TodoList  第1张 上样式: src/TodoList.css

.td-wrapper {
    width: 700px;
    margin: 0 auto;
}

.dp-wrapper {
    width: 100%;
    height: 40px;
    display: flex;
    margin-top: 10px;
}

.dp-wrapper input {
    flex: 4;
    height: 36px;
    line-height: 36px;
    text-indent: 10px;
    font-size: 1rem;
}

.dp-wrapper button {
    flex: 1;
    height: 100%;
    margin-left: 2px;
    font-size: 1rem;
}

.dl-wrapper {
    border: 1px solid gray;
    margin-top: 5px;
}

.dl-wrapper li {
    height: 40px;
    line-height: 40px;
    border-bottom: 1px solid gray;
}

.dl-wrapper li.done {
    text-decoration: line-through;
}

.dl-wrapper li:last-child {
    border-bottom: none;
}

创建工程

npm init vite@latest

后续选择:react + ts 添加必要文件,工程结构如下: Vite+React+TypeScript手撸TodoList  第2张

定义全局数据类型

src/vite-env.d.ts

/// <reference types="vite/client" />

/* 代办事项数据结构 */
interface TodoItem {
    name: string,
    done: boolean
}

/* 通用DOM事件处理器 */
type EventHandler = (e?: SyntheticEvent) => void

/* 处理函数定义:点击提交按钮 */
type UserInputHandler = (userInput: string, e?: SyntheticEvent) => void

/* 处理函数定义:点击列表条目 */
type ImteClickHandler = (index: number, e?: SyntheticEvent) => void

/* 获取指定Item的样式名 */
type ItemClassNameGetter = (index: number) => string

/* 定义DataPicker组件的Props */
interface DataPickerProps {
    onUserInput: UserInputHandler
}

/* 定义DataLister组件的Props */
interface DataListerProps {
    list: TodoItem[],
    onItemClick: ImteClickHandler,
    getClassName: ItemClassNameGetter
}

实现步骤

在App.tsx中加载TodoList:

import { useState } from 'react'
import './App.css'
import TodoList from './TodoList'

function App() {
  return (
    <div className="App">
      <TodoList></TodoList>
    </div>
  )
}

export default App

父组件TodoList具体实现: src/TodoList.tsx

import React, { useState } from 'react'
import DataLister from './DataLister'
import DataPicker from './DataPicker'

/* 引入全局样式 */
import "./TodoList.css"

export default function TodoList() {
    /* 定义全局代办事项列表 */
    const [todoList, setTodoList] = useState([
        { name: "抽中华", done: false },
        { name: "喝剑南春", done: false },
        { name: "烫杀马特", done: true },
    ])

    /* 添加代办事项 */
    const addTodoItem: UserInputHandler = (userInput: string) => {
        setTodoList([
            { name: userInput, done: false },
            ...todoList
        ])
    }

    /* 切换代办事项完成状态 */
    const switchTodoitemState: ImteClickHandler = (index: number) => {
        setTodoList(
            todoList.map(
                (item, i) => (
                    i !== index ? item : { ...item, done: !item.done }
                )
            )
        )
    }

    /* 根据条目完成与否返回不同的样式名 */
    const getTodoitemClassName: ItemClassNameGetter = (index: number) => {
        return todoList[index].done ? "done" : ""
    }

    /* 渲染 */
    return (
        <div className="td-wrapper">
            <h3>TodoList</h3>
            <DataPicker onUserInput={addTodoItem} />
            <DataLister
                list={todoList}
                onItemClick={switchTodoitemState}
                getClassName={getTodoitemClassName}
            />
        </div>
    )
}

用户输入框组件实现: src/DataPicker.tsx

import React, { SyntheticEvent, useState } from 'react'

export default function DataPicker({ onUserInput }: DataPickerProps) {

    /* 定义响应式数据:用户输入的内容 */
    const [userInput, setUserInput] = useState("骚年请输入你的闷响...")

    /* 受控组件的双向数据绑定 */
    const onUserInputChange: EventHandler = (e: SyntheticEvent) => {
        setUserInput((e.target as HTMLInputElement).value)
    }

    /* 处理提交按钮点击事件 */
    const onSubmit: EventHandler = (e: SyntheticEvent) => {

        /* 将用户的输入告知父组件,由父组件自行定夺如何处置之 */
        onUserInput(userInput)

        // 清空用户输入
        setUserInput("")
    }

    /* 渲染 */
    return (
        <div className='dp-wrapper'>
            <input type="text" value={userInput} onChange={onUserInputChange} />
            <button onClick={onSubmit}>提交</button>
        </div>
    )
}

列表展示组件实现: src/DataLister.tsx

export default function DataLister({ list, onItemClick, getClassName }: DataListerProps) {

    return (
        <div className='dl-wrapper'>
            <ul>{
                list.map(
                    (item: TodoItem, index: number) => (
                        <li
                            key={index + item.name}
                            className={getClassName(index)}
                            onClick={() => {
                                /* 告诉父组件第几个item被点击了 具体如何处置由父元素自行决定 */
                                onItemClick(index)
                            }}>
                            {item.name}
                        </li>
                    )
                )
            }</ul>
        </div>
    )
}

源码地址

git clone https://gitee.com/steveouyang/todolist-vite-react-ts.git

项目地址

原文:https://juejin.cn/post/7096184359757021197

打赏
海报

本文转载自互联网,旨在分享有价值的内容,文章如有侵权请联系删除,部分文章如未署名作者来源请联系我们及时备注,感谢您的支持。

转载请注明本文地址:https://www.shouxicto.com/article/3933.html

相关推荐

发布评论

ainiaobaibaibaibaobaobeishangbishibizuichiguachijingchongjingdahaqiandaliandangaodw_dogedw_erhadw_miaodw_tuzidw_xiongmaodw_zhutouganbeigeiliguiguolaiguzhanghahahahashoushihaixiuhanheixianhenghorse2huaixiaohuatonghuaxinhufenjiayoujiyankeaikeliankouzhaokukuloukunkuxiaolandelinileimuliwulxhainiolxhlikelxhqiuguanzhulxhtouxiaolxhwahahalxhzanningwennonuokpinganqianqiaoqinqinquantouruoshayanshengbingshiwangshuaishuijiaosikaostar0star2star3taikaixintanshoutianpingtouxiaotuwabiweifengweiquweiwuweixiaowenhaowoshouwuxiangjixianhuaxiaoerbuyuxiaokuxiaoxinxinxinxinsuixixixuyeyinxianyinyueyouhenghengyuebingyueliangyunzanzhajizhongguozanzhoumazhuakuangzuohenghengzuoyi
支付宝
微信
赞助本站