如何使用 React Hooks 构建待办事项列表
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
本文最初发表于Educative 网站。作者Yazeed Bzadough致力于为开发者创作励志和教育内容,希望通过深入的理解来启发和指导读者。他主要关注 Web 技术,目前包括 JavaScript、TypeScript 和 React。
什么是钩子?
它们是无需 ES6 类即可提供 React 状态和生命周期钩子等功能的函数。
部分好处包括:
- 隔离有状态逻辑,使其更容易测试。
- 无需渲染属性或高阶组件即可共享有状态逻辑。
- 基于逻辑而非生命周期钩子来分离应用程序的关注点。
- 避免使用 ES6 类,因为它们很古怪,实际上并不是类,甚至会让经验丰富的 JavaScript 开发人员也感到困惑。
更多详情请参阅React 官方 Hooks 简介。
请勿在生产环境中使用!截至撰写本文时,Hooks 仍处于 alpha 测试阶段,其 API 随时可能更改。我建议您在业余项目中进行实验,尽情体验 Hooks 的乐趣,但在其稳定之前,请勿将其用于生产代码。
让我们创建一个待办事项清单
待办事项清单之所以被用滥,是有原因的——它们是绝佳的练习工具。我建议无论你想尝试哪种语言或库,都应该先练习一下。
我们的版本只能做几件事:
- 以美观的 Material Design 风格展示待办事项
- 允许通过输入框添加待办事项
- 删除待办事项
设置
以下是GitHub和CodeSandbox的链接。
git clone https://github.com/yazeedb/react-hooks-todo
cd react-hooks-todo
npm install
该分支上有已完成的项目,如果您想跟进,master请查看该分支。start
git checkout start
然后运行项目。
npm start
该应用程序应该运行在[此处应填写运行环境名称]上localhost:3000,这是我们的初始用户界面。
我们已经使用material-ui配置好了页面,让它看起来更专业。接下来,让我们添加一些功能吧!
TodoForm 组件
添加一个新文件,src/TodoForm.js以下是初始代码。
import React from 'react';
import TextField from '@material-ui/core/TextField';
const TodoForm = ({ saveTodo }) => {
return (
<form>
<TextField variant="outlined" placeholder="Add todo" margin="normal" />
</form>
);
};
export default TodoForm;
顾名思义,它的功能就是向我们的状态添加待办事项。说到待办事项,这是我们的第一个亮点。
使用状态
请查看这段代码:
import { useState } from 'react';
const [value, setValue] = useState('');
useState它只是一个接受初始状态并返回数组的函数。请继续使用console.log它。
数组的第一个索引是你的当前状态值,第二个索引是更新函数。
因此我们恰当地给它们命名value,并setValue使用了ES6 解构赋值。
使用表单中的状态
我们的表单应该跟踪输入值,并saveTodo在提交时调用相应的函数。useState可以帮我们实现这个功能吗?
更新TodoForm.js:新代码以粗体显示。
import React, { useState } from 'react';
import TextField from '@material-ui/core/TextField';
const TodoForm = ({ saveTodo }) => {
const [value, setValue] = useState('');
return (
<form
onSubmit={(event) => {
event.preventDefault();
saveTodo(value);
}}
>
<TextField
variant="outlined"
placeholder="Add todo"
margin="normal"
onChange={(event) => {
setValue(event.target.value);
}}
value={value}
/>
</form>
);
};
返回后index.js,导入并使用此组件。
// ...
import TodoForm from './TodoForm';
// ...
const App = () => {
return (
<div className="App">
<Typography component="h1" variant="h2">
Todos
</Typography>
<TodoForm saveTodo={console.warn} />
</div>
);
};
现在,您的值已在提交后记录(按回车键)。
使用待办事项状态
我们还需要为待办事项设置状态。导入相关useState模块index.js。初始状态应该是一个空数组。
import React, { useState } from 'react';
// ...
const App = () => {
const [todos, setTodos] = useState([]);
// ...
};
待办事项列表组件
创建一个名为 . 的新文件src/TodoList.js。编辑:感谢Takahiro Hata帮助我将 onClick 移动到正确的位置!
import React from 'react';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import Checkbox from '@material-ui/core/Checkbox';
import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete';
const TodoList = ({ todos, deleteTodo }) => (
<List>
{todos.map((todo, index) => (
<ListItem key={index.toString()} dense button>
<Checkbox tabIndex={-1} disableRipple />
<ListItemText primary={todo} />
<ListItemSecondaryAction>
<IconButton
aria-label="Delete"
onClick={() => {
deleteTodo(index);
}}
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
);
export default TodoList;
它需要两个道具
Todos待办事项数组。我们遍历map每个待办事项并创建一个列表项。DeleteTodo点击待办事项会IconButton触发此函数。它会传递一个唯一标识符index,该标识符将用于在我们的列表中唯一标识一个待办事项。
将此组件导入到您的系统中index.js。
import TodoList from './TodoList';
import './styles.css';
const App = () => {
//...
};
App然后像这样在你的函数中使用它:
<TodoForm saveTodo={console.warn} />
<TodoList todos={todos} />
添加待办事项
仍然在index.js,让我们编辑我们的TodoForm's属性,saveTodo。
<TodoForm
saveTodo={(todoText) => {
const trimmedText = todoText.trim();
if (trimmedText.length > 0) {
setTodos([...todos, trimmedText]);
}
}}
/>
只需将现有的待办事项与我们新的待办事项合并,多余的空白区域将被删除。
现在可以添加待办事项了!
清除输入
注意:添加新待办事项后,输入内容不会清除。这用户体验很差!
我们可以通过对代码进行少量更改来解决这个问题TodoForm.js。
<form
onSubmit={(event) => {
event.preventDefault();
saveTodo(value);
setValue('');
}}
/>
待办事项保存后,将表单状态设置为空字符串。
现在看起来不错!
删除待办事项
TodoList提供每个待办事项index,因为这是找到我们要删除的事项的可靠方法。
TodoList.js
<IconButton
aria-label="Delete"
onClick={() => {
deleteTodo(index);
}}
>
<DeleteIcon />
</IconButton>
我们将利用这一点index.js。
<TodoList
todos={todos}
deleteTodo={(todoIndex) => {
const newTodos = todos.filter((_, index) => index !== todoIndex);
setTodos(newTodos);
}}
/>
任何与提供的待办事项不匹配的事项index都会被保留并存储在状态中setTodos。
删除功能已完成!
抽象待办事项使用状态
我之前提到过,Hooks非常适合分离状态和组件逻辑。下面展示一下它在我们的待办事项应用中可能的样子。
创建一个名为“.”的新文件src/useTodoState.js。
import { useState } from 'react';
export default (initialValue) => {
const [todos, setTodos] = useState(initialValue);
return {
todos,
addTodo: (todoText) => {
setTodos([...todos, todoText]);
},
deleteTodo: (todoIndex) => {
const newTodos = todos.filter((_, index) => index !== todoIndex);
setTodos(newTodos);
}
};
};
代码和之前一样index.js,只是分离了!我们的状态管理不再与组件紧密耦合。
现在直接导入即可。
import React from 'react';
import ReactDOM from 'react-dom';
import Typography from '@material-ui/core/Typography';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
import useTodoState from './useTodoState';
import './styles.css';
const App = () => {
const { todos, addTodo, deleteTodo } = useTodoState([]);
return (
<div className="App">
<Typography component="h1" variant="h2">
Todos
</Typography>
<TodoForm
saveTodo={(todoText) => {
const trimmedText = todoText.trim();
if (trimmedText.length > 0) {
addTodo(trimmedText);
}
}}
/>
<TodoList todos={todos} deleteTodo={deleteTodo} />
</div>
);
};
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
一切都照常运转。
抽象表单输入 useState
我们也可以用同样的方法修改表格!
创建一个新文件,src/useInputState.js。
import { useState } from 'react';
export default (initialValue) => {
const [value, setValue] = useState(initialValue);
return {
value,
onChange: (event) => {
setValue(event.target.value);
},
reset: () => setValue('')
};
};
现在TodoForm.js应该看起来像这样。
import React from 'react';
import TextField from '@material-ui/core/TextField';
import useInputState from './useInputState';
const TodoForm = ({ saveTodo }) => {
const { value, reset, onChange } = useInputState('');
return (
<form
onSubmit={(event) => {
event.preventDefault();
saveTodo(value);
reset();
}}
>
<TextField
variant="outlined"
placeholder="Add todo"
margin="normal"
onChange={onChange}
value={value}
/>
</form>
);
};
export default TodoForm;
好了,就到这里!希望你们喜欢,下次再见!
如果您想了解更多关于使用 Hooks 的信息,可以访问“使用Hooks 的高级 React 模式”页面。此外,如果您想了解 Yazeed 的更多作品,可以查看他的课程“使用 RamdaJS 的函数式编程模式”。
文章来源:https://dev.to/educative/how-to-build-a-todo-list-with-react-hooks-42dc

