七天快速学完mini-react ,再也不担心不会原理了(实战完结)

# 七天快速学完mini-react ,再也不担心不会原理了(第一天)

# 七天快速学完mini-react ,再也不担心不会原理了(第二天)

# 七天快速学完mini-react ,再也不担心不会原理了(第三天)

# 七天快速学完mini-react ,再也不担心不会原理了(第四天)

# 七天快速学完mini-react ,再也不担心不会原理了(第五天)

# 七天快速学完mini-react ,再也不担心不会原理了(第六天)

# 七天快速学完mini-react ,再也不担心不会原理了(第七天)

第八天:实现 todo-list

用 mini-react 实现 todo-list

我们先来实现静态页面,然后再去实现事件相关

我们先创建一个文件夹srcsrc里有个Todos.jsx

js 复制代码
import React from "../core/React.js"

function Todos() {
  const todos = [
    {
      title: "吃饭",
    },
    {
      title: "喝水",
    },
    {
      title: "写代码",
    },
  ]
  return (
    <div>
      <h1>TODOS</h1>
      <div>
        <input type="text" />
        <button>ADD</button>
      </div>
      <ul>
        {...todos.map(todo => {
          return <li>{todo.title}</li>
        })}
      </ul>
    </div>
  )
}

export default Todos

然后我们导入一下,这里注意,因为是jsx文件,需要去获取一下React这个方法,所以我们一般都需要手动导入一下

js 复制代码
import React from "./core/React.js"

import Todos from "./src/Todos.jsx"

function App() {
  return (
    <div>
      <Todos />
    </div>
  )
}

export default App

运行一下,没有什么问题

接下来就直接上代码吧,这里增加了添加,删除,取消,完成这四个功能

js 复制代码
import React from "../core/React.js"

function Todos() {
  const [todos, setTodos] = React.useState([
    {
      title: "吃饭",
      id: crypto.randomUUID(),
      status: "active",
    },
    {
      title: "喝水",
      id: crypto.randomUUID(),
      status: "done",
    },
    {
      title: "写代码",
      id: crypto.randomUUID(),
      status: "done",
    },
  ])
  const [inputVal, setInputVal] = React.useState("")

  // 添加todo
  function addTodo(title) {
    setTodos(todos => [...todos, { title }])
  }
  // 添加按钮
  function handleAdd() {
    addTodo(inputVal)
    setInputVal("")
  }
  // 删除功能
  function removeTodo(id) {
    const newTodos = todos.filter(todo => todo.id !== id)
    setTodos(newTodos)
  }
  // 完成功能
  function doneTodo(id) {
    const newTodos = todos.map(todo => {
      if (todo.id === id) {
        todo.status = "done"
      }
      return todo
    })
    setTodos(newTodos)
  }
  // 取消功能
  function cancelTodo(id) {
    const newTodos = todos.map(todo => {
      if (todo.id === id) {
        todo.status = "active"
      }
      return todo
    })
    setTodos(newTodos)
  }
  return (
    <div>
      <h1>TODOS</h1>
      <div>
        <input type="text" value={inputVal} onChange={e => setInputVal(e.target.value)} />
        <button onClick={handleAdd}>ADD</button>
      </div>
      <ul>
        {...todos.map(todo => {
          return <TodoItem todo={todo} removeTodo={removeTodo} cancelTodo={cancelTodo} doneTodo={doneTodo}></TodoItem>
        })}
      </ul>
    </div>
  )
}

function TodoItem({ todo, removeTodo, cancelTodo, doneTodo }) {
  return (
    <li className={todo.status}>
      <span>{todo.title}</span>
      <button
        onClick={() => {
          removeTodo(todo.id)
        }}
      >
        remove
      </button>
      <button
        onClick={() => {
          cancelTodo(todo.id)
        }}
      >
        cancel
      </button>
      <button
        onClick={() => {
          doneTodo(todo.id)
        }}
      >
        done
      </button>
    </li>
  )
}
export default Todos

并且我们在style.css中新加了样式

css 复制代码
.done {
  text-decoration: line-through;
  font-weight: 1000;
}
.active {
  color: red;
}
button {
  margin-left: 10px;
}

引入样式

js 复制代码
import React from "./core/React.js"

import Todos from "./src/Todos.jsx"
import "./style.css"

function App() {
  return (
    <div>
      <Todos />
    </div>
  )
}

export default App

当我们运行的时候,发现事件不好使了,通过debugger,我们发现在更新的时候我们去判断一下fiber.dom存在

js 复制代码
function commitWork(fiber) {
  if (!fiber) return

  let fiberParent = fiber.parent
  while (!fiberParent.dom) {
    fiberParent = fiberParent.parent
  }

  if (fiber.effectTag === "update" && fiber.dom) {
    updateProps(fiber.dom, fiber.props, fiber.alternate?.props)
  } else if (fiber.effectTag === "placement") {
    if (fiber.dom) {
      fiberParent.dom.append(fiber.dom)
    }
  }
  commitWork(fiber.child)
  commitWork(fiber.sibling)
}

修改完后,运行执行,按钮功能也是运行成功了!

接下来我们继续增加了一些功能,例如保存数据、切换数据,并且我们还使用了useEffect去初始化数据,已经去判断当radio切换时,切换数据

以下是完整代码:

js 复制代码
import React from "../core/React.js"

function Todos() {
  const [todos, setTodos] = React.useState([])
  const [inputVal, setInputVal] = React.useState("")
  const [displayTodos, setDisplayTodos] = React.useState([])
  const [filter, setFilter] = React.useState("all")
  React.useEffect(() => {
    const rawTodos = localStorage.getItem("todos")
    if (rawTodos) {
      setTodos(JSON.parse(rawTodos))
    }
  }, [])

  // 创建todo
  function createTodo(title) {
    return { title, id: crypto.randomUUID(), status: "active" }
  }
  // 添加todo
  function addTodo(title) {
    setTodos(todos => [...todos, createTodo(title)])
  }
  // 添加按钮
  function handleAdd() {
    addTodo(inputVal)
    setInputVal("")
  }
  // 删除功能
  function removeTodo(id) {
    const newTodos = todos.filter(todo => todo.id !== id)
    setTodos(newTodos)
  }
  // 完成功能
  function doneTodo(id) {
    const newTodos = todos.map(todo => {
      if (todo.id === id) {
        todo.status = "done"
      }
      return todo
    })
    setTodos(newTodos)
  }
  // 取消功能
  function cancelTodo(id) {
    const newTodos = todos.map(todo => {
      if (todo.id === id) {
        todo.status = "active"
      }
      return todo
    })
    setTodos(newTodos)
  }
  // 保存功能
  function saveTodo() {
    localStorage.setItem("todos", JSON.stringify(todos))
  }
  // 切换过滤器
  React.useEffect(() => {
    if (filter === "all") {
      setDisplayTodos(todos)
    } else {
      const newTodo = todos.filter(todo => todo.status === filter)
      setDisplayTodos(newTodo)
    }
  }, [filter, todos])
  return (
    <div>
      <h1>TODOS</h1>
      <div>
        <input type="text" value={inputVal} onChange={e => setInputVal(e.target.value)} />
        <button onClick={handleAdd}>add</button>
        <button onClick={saveTodo}>save</button>
      </div>
      <div>
        <input type="radio" name="filter" id="all" checked={filter === "all"} onChange={() => setFilter("all")} />
        <label htmlFor="all">all</label>
        <input
          type="radio"
          name="filter"
          id="active"
          checked={filter === "active"}
          onChange={() => setFilter("active")}
        />
        <label htmlFor="active">active</label>
        <input type="radio" name="filter" id="done" checked={filter === "done"} onChange={() => setFilter("done")} />
        <label htmlFor="done">done</label>
      </div>
      <ul>
        {...displayTodos.map(todo => {
          return <TodoItem todo={todo} removeTodo={removeTodo} cancelTodo={cancelTodo} doneTodo={doneTodo}></TodoItem>
        })}
      </ul>
    </div>
  )
}

function TodoItem({ todo, removeTodo, cancelTodo, doneTodo }) {
  return (
    <li className={todo.status}>
      <span>{todo.title}</span>
      <button
        onClick={() => {
          removeTodo(todo.id)
        }}
      >
        remove
      </button>
      <button
        onClick={() => {
          cancelTodo(todo.id)
        }}
      >
        cancel
      </button>
      <button
        onClick={() => {
          doneTodo(todo.id)
        }}
      >
        done
      </button>
    </li>
  )
}
export default Todos

运行一下,非常完美

在执行useEffect初始化的时候有一个错误,解决方法是

useEffectdeps为空时,当数据发生改变也需要重新渲染视图

js 复制代码
function workLoop(deadline) {
  let shouldYield = false
  while (!shouldYield && nextWorkOfUnit) {
    nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)

    if (wipRoot?.sibling?.type === nextWorkOfUnit?.type) {
      nextWorkOfUnit = undefined
    }

    shouldYield = deadline.timeRemaining() < 1
  }

  if (!nextWorkOfUnit && wipRoot) {
    commitRoot()
  }

  if (nextWorkOfUnit && !wipRoot) {
    wipRoot = currentRoot
  }

  requestIdleCallback(workLoop)
}

到目前为止,所有的功能就完成,非常感谢大家的阅读和喜欢,后面我还会继续努力,去分享更多技术的

以下是代码仓库,已经分类到每一天了 # 代码仓库

相关推荐
@大迁世界9 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路18 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug21 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213823 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中1 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端