React hooks 通过自定义hooks来复用状态,从而解决了类组件有些时候难以复用的问题。下面我们使用react的hook钩子函数做一个简单的todoList,仅仅使用functional组件和新的useState react hook。
创建项目
使用react-cli构建项目,我们跳过所有手动构建配置过程,具体步骤可以查看react官网
npx create-react-app todo-app
用useState Hook添加State
State允许我们来跟踪变化在React组件中。一个todo列表经常变化。例如:
- 添加新的待办事项
- 改变现有待办事项
- 删除待办事项
- 完成待办事项
- 未完成待办事项
接下来在App.js文件的顶部引入useState hook
import React, { useState } from 'react';
初始化组件的state属性,如下:
App.js
...
function App() {
const [todos, setTodos] = useState([
{
content: '起床',
isCompleted: true,
},
{
content: '吃饭',
isCompleted: false,
},
{
content: '跑步',
isCompleted: false,
}
]);
...
当你使用useState hook ,将会添加两个值:getter和setter。在上面的代码中,todos是state本身,setTodos 是更新state 值的方法。
新建待办事项
根据数据,循环todos数组,在界面展示待办事项列表,通过监听input的onKeyDown事件来处理列表的新建、更新、删除操作。
...
<form className="todo-list">
<ul>
{todos.map((todo, i) => (
<div className="todo">
<div className="checkbox" />
<input type="text" value={todo.content} onKeyDown={e => handleKeyDown(e, i)/>
</div>
))}
</ul>
</form>
...
todos主要功能
更新操作
使用复制出来的待办事件,插入一个新的空待办事项后选中当前待办事项,然后更新原始数组的副本,聚焦input。
function createTodoAtIndex(e, i) {
const newTodos = [...todos]; // tips:此处创建一个副本todos状态数组是因为state不应该直接修改
newTodos.splice(i + 1, 0, {
content: '',
isCompleted: false,
});
setTodos(newTodos);
setTimeout(() => {
document.forms[0].elements[i + 1].focus();
}, 0);
}
tips:此处使用延时是因为更新内部状态的组件不发生瞬间反应,因此我们需要时间等待state完成更新。
删除操作
todos values内容为空之后输入退格键,删除这个todos的功能,代码实现如下:
function removeTodoAtIndex(i) {
if (i === 0 && todos.length === 1) return;
setTodos(todos => todos.slice(0, i).concat(todos.slice(i + 1, todos.length)));
setTimeout(() => {
document.forms[0].elements[i - 1].focus();
}, 0);
}
通过todoList的编写我们不难发现函数式编程风格代码量更少,更优雅。另外,对比类组件,函数组件里面的unused状态和unused-method更容易被发现。当然缺点也很明显,状态不同步即文中提到的延时操作。
每天学习一点点,前端之路不迷茫…