React 待办事项列表 | 豆包MarsCode AI刷题

50 阅读2分钟

创建项目

  1. 首先通过vite脚手架启动一个react项目
pnpm create vite
  1. 安装tailwindcssdaisyui
npm install -D tailwindcss
npx tailwindcss init

npm i -D daisyui@latest

编写代码

  1. 编写基本结构样式

这里用到了语义化标签 :)

image.png

<div className="p-4 w-96 h-96 bg-slate-300 mx-auto mt-32 rounded-lg font-bold">
    <h1 className="text-2xl text-center mb-2">待办事项</h1>
    <header className="flex">
      <input value={inputValue} onKeyDown={useKeydownHook(add, 'Enter')} onChange={(e) => setInputValue(e.target.value)} type="text" placeholder="Type here" className="input input-bordered w-full max-w-xs" />
      <button onClick={add} className="btn ml-2">添加</button>
    </header>
    <main>
      {
        todoList.map( (val, idx) => <Item key={val.id} value={val} index={idx} onCheck={onCheck} onDelete={onDelete} /> )
      }
    </main>
  </div>
  1. 封装待办事项子组件

这里其实不用单独封装,统一写成一整个待办事项列表更加方便,将列表拆开成单个反而需要额外的通信开销。不过笔者为了熟悉react,多做了一步

image.png

    <div className="p-4 rounded-xl flex justify-between items-center mt-4 bg-white">
      <div className="flex">
        <input onChange={onCheckChange} checked={value.isDone} type="checkbox" className="checkbox checkbox-primary" />
        <label className="ml-2">{ value.title }</label>
      </div>
      <button onClick={onDelete} className="btn btn-sm btn-error">删除</button>
    </div>
  1. 编写业务逻辑

通过数组下标来实现父子通信,其实原理和封装成整个列表一样,只不过这里需要单独将待办项的index传递过去

const add = () => {
  if(!inputValue) return
  setTodoList([...todoList, {
    id: new Date().getTime(),
    title: inputValue,
    isDone: false
  }])
  setInputValue("")
}

const onCheck = (id: number) => {
  const newTodoList = [...todoList]
  newTodoList[id].isDone =!newTodoList[id].isDone
  setTodoList(newTodoList)
}

const onDelete = (id: number) => {
  const newTodoList = [...todoList]
  newTodoList.splice(id, 1)
  setTodoList(newTodoList)
}
  1. 本地持久化
const [inputValue, setInputValue] = useState("")
const [todoList, setTodoList] = useState<TodoItem[]>([])

useEffect(() => {
  const localTodoList = localStorage.getItem("todoList")
  if(localTodoList) {
    setTodoList(JSON.parse(localTodoList))
  } 
}, [])

useEffect(() => {
  localStorage.setItem("todoList", JSON.stringify(todoList))
}, [todoList])
  1. 封装hook

每次键盘打完字都需要操作鼠标去添加不太方便,于是想到了监听键盘的回车事件。搜了一下发现react并没有vue的@click.enter的语法糖。需要在keydown事件处理函数里面单独去判断key的值,有些麻烦。为了练手,这里决定多此一举封装一个keydown Hook。

function useKeydownHook(callback: () => void, type: string) {
  return (e: React.KeyboardEvent) => {
    if(e.key === type) {
      callback()
    }
  }
}

这样就可以灰常方便的绑定特定的键盘值了

<input onKeyDown={useKeydownHook(add, 'Enter')} />

总结

在该demo中用到了以下知识点:

  • tsx
  • useState
  • useEffect
  • react的class、事件监听
  • 父子组件通信,函数式组件
  • 自定义Hook

image.png