Hooks 解决的问题
Hooks 要解决的问题是状态共享,是继 render props 和 HOC 之后的第三种状态共享方案;
hooks 是通过封装状态的使用逻辑,实现状态共享,也因此让组件的状态与 UI 分离。状态都存在于自定义 hooks 里,而不存在于函数式组件里,因此状态的使用逻辑可以在不同的组件间复用
// 状态使用逻辑部分
export function useToggleForm(initStatus: boolean): [boolean, noopfn] {
const [expand, setExpand] = useState(initStatus);
function handleToggleForm() {
setExpand(!expand);
}
return [expand, handleToggleForm];
}
// UI 部分
<Button onClick={handleToggleForm}>
{expandForm
? intl.get('hzero.common.button.collected').d('收起查询')
: intl.get(`hzero.common.button.viewMore`).d('更多查询')}
</Button>
可以看到状态 expand 不存在于组件中,而是存在于自定义 hooks 中
Hooks 的优点
- 共享逻辑:原因如上
- 更新粒度更新:?
Hooks 缺点
- 依赖传染性依赖传染性,这导致了开发复杂性的提高、可维护性的降低
- 缓存雪崩,这导致运行性能的降低
- 异步任务下无法批量更新,这也会导致运行性能的降低
Hooks 每次运行创建一个全新的闭包,缓存闭包内所有的数据,包括 UI 状态(例如 <input /> 组件状态)
Hooks 的使用
state hook
- 使用 useState 声明多个 state 或将状态合并到一个对象中都可以,需要注意的是更具需要拆分状态,以便封装状态逻辑
- useState 每次渲染都会运行,第一次渲染时会返回初始值,重渲染时会记住当前的值并返回
- 函数更新模式:
setCount(c => c + 1); - 惰性初始 state:当初始 state 需要通过复杂运算获得时使用
effect hook
- useEffect 在 React 更改 DOM 之后运行回调,执行副作用
- 可以使用多次 useEffect,注意将相关逻辑写在一个 useEffect 中
- 每次重渲染,都会生产新的 effect,某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲
- React 会在执行当前 effect 之前对上一个 effect 进行清除
- 由于尽量不要将函数加入依赖数组,所以当 useEffect 中使用函数时:
- 在 useEffect 内部声明函数
useCallback
- useCallback 是为了避免函数作为 props 时传递给子组件,造成子组件不必要的渲染
- 如何从 useCallback 读取一个经常变化的值:
稳定的函数(在 hooks 中使用,而不需要加入依赖数组)
- setState:例如
const [count, setCount] = useState(0)中的setCount - useReducer 的 dispatch
useMemo
- 性能优化用法
function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
一些自定义 hooks
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}