原文地址:How to Build a Todo List with React Hooks css
React v16.7.0-alpha 引入了钩子(Hooks)。开心!html
钩子是能让你在没有用es6类的状况下使用React的状态, 生命周期钩子这些特性的功能。react
优点:git
查看更多:React’s official Hooks introes6
写这篇文章时,钩子还处于内部测试(alpha)阶段。它们的API随时均可能改变。
我建议你在你的业余项目中体验钩子,在它们成为稳定版本以前,不要在线上代码中使用。github
待办事项清单是使用普遍的例子,理由很充分——它们是很棒的练习工具。不管你想尝试任何语言或库我都推荐使用它。
在这个例子中,咱们只实现其中的一小部分功能:npm
这是github和CodeSandbox的地址数组
git clone https://github.com/yazeedb/react-hooks-todo cd react-hooks-todo npm install
master分支已经实现了这些功能,若是你想本身跟着实现,请切到start分支。dom
git checkout start
启动工程函数
npm start
这个应用应该跑在localhost:3000
上,这是初始UI:
咱们已经设置了material-ui来给页面一个专业的外观,如今咱们加入更多功能!
添加一个新文件,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
是一个接收初始状态(state)返回一个数组的函数。console.log
它吧。
数组的第一个值是你的state如今的值,第二个值是state的更新方法。
因此咱们把它们叫作value
和setValue
, 并使用es6解构赋值对它们进行赋值。
咱们的表单应该跟踪input的值并在保存提交时执行saveTodo
方法。useState
能帮咱们实现它。
更新updateForm.js
, 这是更新以后的代码:
import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; const TodoForm = ({ saveTodo }) => { <b>const [value, setValue] = useState('');</b> return ( <form <strong>onSubmit={event => {</strong> event.preventDefault(); saveTodo(value); }} > <TextField variant="outlined" placeholder="Add todo" margin="normal" onChange={event => { setValue(event.target.value); }} value={value} /> </form> ); }; export default TodoForm;
回到index.js
,引入而且使用这个组件。
... import TodoForm from './TodoForm'; ... const App = () => { return ( <div className="App"> <Typography component="h1" variant="h2"> Todos </Typography> <TodoForm saveTodo={console.warn} /> </div> ); };
如今你在input输入的值已经能够被打印出来了。(记得敲enter哦)
咱们的事项列表todos也须要state。在index.js
中引入useState
。初始state应该是空数组。
import React, { useState } from 'react'; ... const App = () => { const [todos, setTodos] = useState([]); return ...
创建一个新文件:src/TodoList.js
大部分代码是来自Material-UI库的高级组件, 这是更新以后的代码:
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;
它接收两个属性:
在index.js
中引入这个组件。
... import TodoList from './TodoList'; import './styles.css'; const App = () => { ...
并在App
方法中使用它:
... <TodoForm saveTodo={console.warn} /> <TodoList todos={todos} />
仍是在index.js
中,编辑TodoForm
的属性, saveTodo
:
<TodoForm saveTodo={todoText => { const trimmedText = todoText.trim(); if (trimmedText.length > 0) { setTodos([...todos, trimmedText]); } }} />
这里咱们只是把空格去掉,把新的值添加到todos
中。
咱们如今能够添加事项了!
如今添加新的事项后,咱们没有把input清空,这是很差的用户体验!
咱们只须要在TodoForm.js
中作一点小改动,就能够修复它。
<form onSubmit={event => { event.preventDefault(); saveTodo(value); setValue(''); }} >
当事项被保存后,咱们就把form的state变成空字符串。
如今看起来很好了!
TodoList
为每一条事项都提供了索引,根据索引咱们能找到咱们想删除的事项。
// 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); }} />
咱们使用setTodos
方法把全部不符合index
的事项保存下来。
删除功能完成!
文章开头我提到 钩子便于分离状态和组件的逻辑。因此咱们在这个应用中能够这样作。
新建一个文件叫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);
如今一切工做正常!
咱们能够对表单作一样的处理!
新建一个文件: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;
如今咱们所有完成了!
但愿你喜欢!!
谢谢!
做者:Yazeed Bzadough