原文地点:How to Build a Todo List with React Hooks
React v16.7.0-alpha 引入了钩子(Hooks)。高兴!
什么是钩子(Hooks)?
钩子是能让你在没有用es6类的情况下运用React的状况, 生命周期钩子这些特征的功用。
上风:
- 断绝状况相干逻辑,使测试越发轻易
- 不须要运用衬着属性或许高阶组件就能够同享状况相干逻辑
- 依据逻辑而不是生命周期钩子来星散运用程序的关注点
- 防止ES6类,由于它们很新鲜,不是真正的类,甚至会误导有履历的JavaScript开发人员
检察更多:React’s official Hooks intro
不要在临盆环境运用
写这篇文章时,钩子还处于内部测试(alpha)阶段。它们的API随时都能够转变。
我发起你在你的业余项目中体验钩子,在它们成为稳固版本之前,不要在线上代码中运用。
构建事项列表
待办事项清单是运用普遍的例子,来由很充足——它们是很棒的演习东西。不管你想尝试任何言语或库我都引荐运用它。
在这个例子中,我们只完成个中的一小部分功用:
- 运用Material Design展现事项列表
- 经由过程input增加事项
- 删除事项
设置
这是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
上,这是初始UI:
我们已设置了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;
经由过程组件名字,我们就晓得它是用来增加事项的,它也就是我们的第一个钩子。
useState
看这段代码:
import { useState } from 'react';
const [value, setValue] = useState('');
useState
是一个吸收初始状况(state)返回一个数组的函数。console.log
它吧。
数组的第一个值是你的state如今的值,第二个值是state的更新要领。
所以我们把它们叫做value
和setValue
, 并运用es6解构赋值对它们举行赋值。
对表单(Form)运用useState
我们的表单应当跟踪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)运用useState
我们的事项列表todos也须要state。在index.js
中引入useState
。初始state应当是空数组。
import React, { useState } from 'react';
...
const App = () => {
const [todos, setTodos] = useState([]);
return ...
TodoList组件
竖立一个新文件: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;
它吸收两个属性:
- todos: 事项数组,我们遍历数组,竖立每个事项
- deleteTodo: 点击一个事项的删除按钮触发这个要领,它吸收一个参数: 索引,这个索引唯一标识每个事项。
在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框
如今增加新的事项后,我们没有把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
的事项保留下来。
删除功用完成!
抽取事项列表(todos)的useState
文章开首我提到 钩子便于星散状况和组件的逻辑。所以我们在这个运用中能够如许做。
新建一个文件叫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);
如今一切工作一般!
抽取表单input中的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;
如今我们悉数完成了!
愿望你喜好!!
感谢!
作者:Yazeed Bzadough