今天写代码写到 Hook 函数的时候,突然感觉 Hook 函数是真的香。感觉已经回不去了。所以今天顺便整理下一些 Hooks 函数。
Hooks 的起因
React 团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。 组件的最佳写法应该是函数,而不是类。
React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。
useState
主要就是替代原先的 state 和 setState API。
- useState() 函数中的参数是初始值。
- useState() 函数的参数可以是带有返回值的函数。
- useState() 返回一个数组,数组第一个值类似于 state,第二个值类似于 setState。
- 可以进行类型定义:
const [text, setText] = useState<string>('hello world')
下面是一个受控输入框的例子:
import React, { useState } from 'react'
import { Input } from 'antd'
export default () => {
const [text, setText] = useState('hello world')
return (
<div>
<div>内容是:{text}</div>
<Input value={text} onChange={(e) => setText(e.target.value)} />
</div>
)
}
useEffect
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
}, [value]);
useContext
接收一个 Context 对象并返回当前 Context 的值。
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
useReducer
其实就是 redux 的 hook 版本,类似于 useState,但是数据会放到 redux 中。函数有三个参数,分别是 reducer 分发事件,initialCount 初始化数据,init 是初始化事件。返回 redux 当前数据 state 以及 dispatch 函数用于改变 redux 的 state 值。
function init(initialCount) { return {count: initialCount};}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset': return init(action.payload); default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init); return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
将回调函数分成函数和传参传给 useCallback 函数,只有当传参发生变化时,回调函数才会执行。算是对回调函数的一种优化了。
useCallback(fn, deps)
相当于useMemo(() => fn, deps)
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
它一般用于计算,会在参数发生变化是重新计算。它执行在渲染期间,所以不要滥用。它是一种性能优化的方式(感觉类似于是 Vue 的 computed 属性)。
useCallback
返回优化版的回调函数,而useMemo
返回优化版的计算数值。或者说,useMemo
返回的是函数运行的结果,useCallback
返回的是函数。
useRef
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useRef 返回一个可变的 ref 对象,函数中的参数是它的初始值。这个方法用来获取 ref 操作 DOM 非常好用。算是最新的获取 ref 的方式了。
useImperativeHandle
useImperativeHandle
可以让你在使用 ref
时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle
应当与 forwardRef
一起使用:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
在本例中,渲染 <FancyInput ref={inputRef} />
的父组件可以调用 inputRef.current.focus()
。
暴露 ref 给父组件,父组件可以调用 useImperativeHandle 里面定义的函数。
useLayoutEffect
它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。
useDebugValue
useDebugValue
可用于在 React 开发者工具中显示自定义 hook 的标签。它一般用在自定义 Hook 函数时。但是不推荐频繁使用。
自定义 Hook
之前,在 React 中有两种流行的方式来共享组件之间的状态逻辑: render props 和高阶组件,而现在自定义 Hook 也能实现这一点。
一些自定义 Hook 的点:
- 函数命名必须使用 use 开头。
- 自定义 hook 函数本质上还是正常的函数,只是可以在里面使用 Hook API 的东西,在提取和实现逻辑。函数的传参和返回值和其他函数是一样的。
- 两个组件使用相同的 Hook 是不会共享 state 的!
感觉用好了自定义 Hook 会让代码更加的优雅整洁。回头可以试着抽一些自定义 Hook 出来。
其他
一开始我不理解为什么 Hook 函数通常返回的是一个数组,后来发现是为了方便命名 Hook 函数中的内容,配合上数据解构特别方便。如果返回的是个对象,那么对象名称就被定死了。
最后
以上就是我对于 React Hook 的整理笔记和学习笔记,总体上感觉 Hook 函数真的很好用。实际用下来明显感觉代码轻量了很多。按照需要使用 Hook 函数也让写代码更加的自由。而自定义 Hook 也提供了一种很好的共享逻辑抽象方案。值得学习和使用。真香~
网友评论