Why Hooks
Function Component可以使用状态- 简化
Class Component的生命周期 - 无需为
this指向问题伤破脑经 - 解决
render props&HOC嵌套缺陷 - 官方爸爸让你学
How to use Hooks
现在开始我们用创建一个计数器的方式开始学习使用
React Hooks
useState
useState 的用法很简单,传入一个初始 state,返回一个 state 以及修改 state 的函数。
// useState 返回的 state 是个常量
// 每次组件重新渲染之后,当前 state 和之前的 state 都不相同
// 即使这个 state 是个对象
const [count, setCount] = useState(1)
setCount 就相当于 setState ,可以传入一个新的状态或者函数。例如:
setCount(2)
setCount(prevCount => prevCount + 1)
其实 useState 内部可以简单理解为(只是理解):
// 闭包存储 state 数据
function useState(initialState){
let state = initialState;
function setState = (newState, action) => {
state = newState;
}
return [state, setState]
}
那接下来编写 Count 组件:
function Counter() {
const [count, setCount] = React.useState(0)
return (
<div>
Count: {count}
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
</div>
);
}
useEffect
现在我们有了新的需求: 在 count 每次更新的时候打印 count 的值,这时我们需要用 useEffect 来实现
function Counter() {
const [count, setCount] = React.useState(0)
React.useEffect(() => {
console.log(count)
})
return (
<div>
Count: {count}
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
以上在每次重新渲染时, useEffect 就可以实现打印,可以把它当作是 componentDidUpdate 和 componentDidMount, 同于 Class Component中的
componentDidMount() {
console.log(count)
}
componentDidUpdate() {
console.log(count)
}
另外它可以返回一个函数,用于解绑一些副作用,功能类似于 componentWillUnmount
React.useEffect(() => {
console.log(count)
// 注意: 当我们每次更新计数时,都会先打印 clean 这行 log
return () => console.log('clean', count)
})
为了防止我们不必要的渲染,useEffect 加入第二个参数, 只有在 count 改变的时候才会执行。
React.useEffect(() => {
console.log(count)
// 当我们每次更新计数时,都会先打印 clean 这行 log
return () => console.log('clean', count)
}, [count])
useReducer
useReducer 是 useState 的替代方案,主要是为了解决多个 state 子值的问题。(会Redux的一看就会明白)
看一下🌰:
// 初始化 state
const initialState = {count: 0};
// 类于 redux 的 reducer
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
/* dispatch 调用 action 操作 */
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</div>
);
}
useMemo
这是个用于优化的 hook, 返回一个 memoized 值。说白了就是会利用闭包的特性存储上次的参数,如果和这次传入的参数一致就返回之前的结果。这叫做记忆化技术。
下面我们来看个反🌰:
function WithoutMemo() {
const [count, setCount] = useState(1);
const [val, setValue] = useState('');
function expensive() {
console.log('compute');
let sum = 0;
for (let i = 0; i < count * 100; i++) {
sum += i;
}
return sum;
}
return <div>
<h4>{count}-{val}-{expensive()}</h4>
<div>
<button onClick={() => setCount(count + 1)}>+c1</button>
<input value={val} onChange={event => setValue(event.target.value)}/>
</div>
</div>;
}
这个函数式组件面临的问题就是 expensive 方法不管是 count 还是 val 改变时都会由于组件的重新渲染, 重新计算。
但是这里的昂贵计算只依赖于count的值,在val修改的时候,是没有必要再次计算的。在这种情况下,我们就可以使用useMemo,只在count的值修改时,执行expensive计算:
// count 如果不变就不会再次执行
const expensive = React.useMemo(() => {
console.log('compute');
let sum = 0;
for (let i = 0; i < count * 100; i++) {
sum += i;
}
return sum;
}, [count]);
useCallback
useCallback 和 useMemo 很相似,不过它返回的是函数。下面继续看个🌰:
function Parent() {
const [count, setCount] = React.useState(1);
const [val, setVal] = React.useState('');
const callback = React.useCallback(() => {
return count;
}, [count]);
return <div>
<h4>{count}</h4>
// callback 作为 props 传入
<Child callback={callback}/>
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<input value={val} onChange={event => setVal(event.target.value)}/>
</div>
</div>;
}
function Child({ callback }) {
const [count, setCount] = React.useState(() => callback());
React.useEffect(() => {
setCount(callback());
}, [callback]);
return (
<div>
{count}
</div>
)
}
现在就是有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
自定义 Hook
通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
自定义 Hook 是一个函数,其名称以 use 开头,函数内部可以调用其他的 Hook。
function useConsoleCount(count) {
React.useEffect(() => {
console.log(count)
// 当我们每次更新计数时,都会先打印 clean 这行 log
return () => console.log('clean', count)
}, [count])
}
function Counter() {
const [count, setCount] = React.useState(0);
useConsoleCount(count)
return (
<div>
Count: {count}
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
结尾
- 这篇只是简单介绍
hook的使用,有错误的地方欢迎指正 - 关于其他的
hook可直接阅读官方文档