做者最初使用React Hooks
的场景就是用Hooks
重构现有的 Class 组件。javascript
改写 Class 组件也没有很难, 对照官网 API, 直接暴力输出java
useState
替代this.state
useCallback
替代 Class 的方法useEffect
替代生命周期函数首先, 来看一个使用 Class 实现的组件, 支持列表的增删查改(很差意思, 只实现了增);react
按照上面的暴力输出思路和官网提供的Hooks
API, 咱们将一个 Class 组件改形成了 Function + Hooks 组件试试。数组
看上去好像没什么问题, 有一种重构代码大功告成
的感受。可心里总有一点点不安, 这不安来自于哪里呢?架构
在身边的同事看到这段代码后, 吐槽地说了一句, 使用React Hooks看上去好乱阿, 在一个函数里写了一坨代码, 不像OOP那样逻辑清晰
。框架
仔细想一想, 好像确实是这个样子的。 重构后的组件代码逻辑都在同一个函数中, 看上去逻辑不清晰、可阅读性不好、维护困难。ide
这么简单的组件改写后都这么乱了, 逻辑更复杂一些的组件, 就是更大一坨的意大利面了==函数式编程
放弃使用 React Hooks? 难道它就真的不香吗?!
考虑到本身的代码能力是渣中本渣, 因此必定是本身的使用姿式有问题了。那就看看能不能继续优化下去吧!
在上面代码中, 使用useEffect
模拟了componentDidMount
这一辈子命周期。React 官方推荐使用多个useEffect
去完成不一样的事情, 而不是放在一块儿。那咱们能够对此进行必定的封装处理。
// useMount sourcecode
import { useEffect } from 'react';
const noop = () => {};
export function useMount(mount, unmount = noop) {
useEffect(() => {
mount();
return () => {
unmount();
};
}, []);
}
复制代码
// use case
export default function Demo(props) {
useMount(() => {
// do something
// after didMount
});
}
复制代码
在上述组件中, 使用了useState
, 并在添加
,编辑
,删除
等操做中都调用了修改 State 的setXXX
方法。
像这样的数据-操做
有着相关联系的, 咱们能够封装本身的 Hooks
import { useState, useCallback } from 'react';
export function useList(initial = []) {
const [list, setList] = useState(initial);
const add = useCallback(data => {
// setXXX使用函数后, 入参会拿到最新的state数据
setList(list => [...list, data]);
}, []);
const edit = useCallback(data => {}, [list]);
const deleteOne = useCallback(data => {}, [list]);
return [list, add, edit, deleteOne];
}
复制代码
上述数据结构-操做方法
只是最简单的一种方案。更多的使用方法和抽象封装要具体状况具体分析来使用了。
因此在上述封装以后,
export default function Demo() {
const [list, add, edit, deleteOne] = useList([]);
const handleAdd = useCallback(() => {
add({
title: `local data ${list.length}`,
description: 'description test'
});
}, []);
const handleEdit = useCallback(() => {}, []);
const handleDelete = useCallback(() => {}, []);
usetMount(() => {});
//...
// 其余不变
}
复制代码
方法论主要解决“怎么办”的问题。
下文只是我的对React Hooks
的实践总结, 若是有更好的思路欢迎来拍砖。
这里提到的方法论, 是在使用函数组件 + React Hooks时, 咱们该怎么合理的应用它。
首先, 将注意力回到最初提到的函数组件
, 它实际上就是数据=>视图
, 一组特殊的输入输出的函数。
v = f(p)
复制代码
函数组件与React Hooks体现的都是函数式编程的思想, 即:
函数式编程是一种编程范式,它将电脑运算视为函数运算,而且避免使用程序状态以及易变对象。其中,λ 演算(lambda calculus)为该语言最重要的基础。并且,λ 演算的函数能够接受函数看成输入(引数)和输出(传出值)。 比起指令式编程,函数式编程更增强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
在上文中提到了数据 -> 视图 -> SideEffect
这样的流程。
每一次渲染都是不一样状况引发 -> 数据变动 -> 视图更新 -> 执行SideEffect
的过程, 这是一条主线的渲染流程, 那能够进行以下重组:
每一次渲染
=== 多个(数据, 操做指令, 反作用)
=> 视图
=> SideEffect处理
咱们更须要关心(数据, 操做指令, 反作用)
这个元组, 如何将多个元组在这个渲染流程中合并成一条数据 -> 视图 -> SideEffect
是React Fiber架构实现的事情。这个心智操做由React框架解决。咱们只要正确实现(数据, 操做指令, 反作用)
的封装就行了。
而这是咱们可使用React Hooks
作到的事情, 也是咱们如何合理的封装使用React Hooks
的方法论。
我的以为倒计时
是一个不错的例子
import React, { useState, useCallback, useEffect, useRef } from "react";
export function useCount() {
const [count, setCount] = useState(0);
const timer = useRef(null);
useEffect(() => {
timer.current = setTimeout(() => {
if (count > 0) {
setCount(count - 1);
}
}, 1000);
return () => {
clearTimeout(timer.current);
};
}, [count]);
const startCount = useCallback(count => {
setCount(count);
}, []);
const stopCount = useCallback(() => {
clearTimeout(timer.current);
}, []);
return [count, startCount, stopCount];
}
复制代码
咱们使用useCount
这个封装的Hook, 提供了数据->count
, 操做->startCount
, 内部使用useEffect
封装了使用setTimeout
倒计时的逻辑。
本文着重点在于如何合理地使用React Hooks, 提出了对书写函数组件+ReactHooks
的方法论的思考。
封装Hooks也是基于可扩展可维护的实用角度出发。 本文也是提醒本身不要为了写Hooks而写Hooks。