// hooks/useTodos.js
import { useState,u s e E f f e c t } from 'react';const STORAGE_KEY = 'todos';// 常量提取,利于迁移
// --- 防御性工具函数 ---
function loadFromStorage {
const raw = localStorage.getItem;
try { return raw?JSON.parse :;}
catch{ console.warn;return,} // 🛡️ Pain Point : 隐私模式/损坏数据兜底
}
function saveToStorage{
try{ localStorage.setItem);}
catch{ console.error;怎么说呢,} // 🛡️ Pain Point : QuotaExceededError
}
// --- 主 Hook ---
export function u s e T o d o s{
// ✨ Pain Point Solution : 惰性初始化 — — 首渲染只读一次磁盘
const=u s e S t a t e;// ✨ Pain Point Solution : 集中式副作用 — — 数据变即存,杜绝漏存 /重复存
u s e E f f e c t=>{
saveToStorage;},),// --- 对外暴露稳定 API ---
const addTodo==>{
if)return;setTodos,text:text.trim。completed:false}]);// 函数式更新避免闭包陷阱
};const toggleTodo==>{
setTodos);},const deleteTodo==>{
setTodos);},return{todos,addTodo。toggleTodo,deleteTodo};}
// App.jsx
import{u seT o d o s}from './hooks/u seT o d o s';import TodoInput from './components/TodoInput';import TodoList from './components/TodoList';不过,export default function App{
const{todos。addTodo,toggleTodo,d ele teTod o}=u seT odos;// 一行接管全套能力
return(
<>
{/* 输入区完全不知道持久化存在 */}
⟨ TodoInput ad d To do={ad d To do}/ ⟩
{/* 展示区只负责映射 props->UI */}
⟨ todos.length?:}
),}
子组件各司其职——这就是"非代码封装"带来的架构红利:
> TodoInput.jsx < b>>专注受控输入 & 防抖提交
> 内部自管无任何业务感知 < small>>jsx
export default function TodoInput{
const=usestate;const hSub==>{eprev;if)ret,adtodotxt);stex''}
ret
}
td>
td width =\one-third\">
b TodItem.jsx
b
专注单项渲染&事件上报
\small 数据下行,事件上行\small
jsx
exportdefaultfunTdIt{
ret=\u00a7\u00a7gld}
spaclas={to.cmp?'cmp':''}>{tx}\sp
btnonClk={=\u00a7\u00a7tl}删除\u00a7\u00a7btn
/l
)
}
td>
td width =\one-third\">
b TdoLst.jsx
b
puremapper
\"Keyed fragments prevent DOM thrashing\"
small
jxs
expfunTl{
utclas=\"lst\">
tos.mp)
utc
}
jxs
td>
/tr>
table>