最近在研究react 16.7的react hooks功能,写个笔记分享一下,还在观望的同学们可以参考一下
基本上是对官网内容的搬运、翻译、补充,加上自己的实践内容
https://reactjs.org/docs/hooks-intro.html
先说感想:最大的改变就是将代码从按生命周期分组变成了按功能分组,以前一个功能的代码经常要分散在didMount
,didUpdate
,willUnmount
这些生命周期里写,每个生命周期里不同的功能代码混杂在了一起
而现在可以把功能A的代码放在一起写在同一个地方,功能B代码放在一起写在另一个地方
还有一个重大的意义是把函数式组件的生命周期功能补全了,现在class组件能做的功能函数组件基本都能做了(还有少数功能不能,比如componentDidCatch
,但是会在以后的版本中提供此功能,最终函数式组件会覆盖类组件的所有功能),而且代码量会更少,可读性更高
不过把旧的class组件用function形式重写的工作量还是挺大的,幸好可以一个个慢慢改
现在react 16.7还在alpha版本,可以用npm i react@next react-dom@next
更新到此版本
基本用法
首先react hooks只能用在函数式组件中,也就是用function方式写的组件,不能用在class组件中
其次react hooks只能写在在函数组件的最顶层域中,不能写在循环或嵌套的函数内
但是有一个例外,见下文的自定义hook
state hook: useState
直接把官网的例子贴过来讲解,一个简单的点击+1功能
import React from 'react';
const { useState } = React;
function Example() {
// Declare a new state variable, which we'll call "count"
// useState以数组形式返回两个值,[0]是state值,[1]是对应该值的setState函数
// 本例只声明了一个state,如果你需要多个state,那就使用多次useState
const [count, setCount] = useState(0);
// 直接使用count值,用setCount来更新count值(忘掉this.state吧)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
把一个this.state和this.setState变成了多个state和对应的setState函数了
effect hook: useEffect
这个hook算是componentDidMount
,componentDidUpdate
,componentWillUnmount
功能的集合,它会在本组件每次render后执行(didMount和didUpdate)
useEffect主要用于处理组件中有副作用(side effects)的功能
还是贴官网例子,对上例增加一个功能,使当前页面的标题也随着点击改变
import React from 'react';
const { useEffect } = React;
function Example() {
const [count, setCount] = useState(0);
// 在class方式的组件中,我们需要在componentDidMount和componentDidUpdate两个函数中写同样的代码
// 现在,只需要在useEffect中写一遍就可以了
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
// 如果想在组件被卸载的时候将标题恢复,那么可以返回一个函数(对应componentWillUnmount)来处理
// return () => document.title = 'old title';
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
还有其他的用法:一个组件在挂载时需要addListener,卸载时需要removeListener,以前的做法时分别在componentDidMount
和componentWillUnmount
分别写,现在可以用useEffect
写在一起
useEffect(() => {
document.addEventListener(type, listener);
return () => {
document.removeEventListener(type, listener);
};
});
例:实现onChange:
每次组件内的值改变,就调用一次onChange
function Example({ onChange }) {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
useEffect(() => {
onChange(a, b);
}, [a, b]);
return (
<>
a:<Input value={a} onChange={setA} />
b:<Input value={b} onChange={setB} />
</>
);
}
这里要使用到useEffect
的第二个参数,这是一个数组,功能类似于shouldComponentUpdate
,如果组件的两次更新之间这个数组内的值没有发生任何变化(使用===逐个判断),那么这次更新就不会调用这个useEffect的函数,用于性能优化,在绝大多数情况下建议传入这个参数,否则每次render都会调用一次,在本例这样的情况中还会导致死循环的出现(onChange导致父组件更新->父组件更新导致本组件更新->本组件更新导致onChange触发,我重构第一个组件时就踩了这个坑)
context hook: useContext
这个hook很简单:
const context = useContext(Context);
其中,Context
参数是React.createContext
的返回值
然后正常使用
其他hook
useReducer
useCallback
useMemo
useRef
useImperativeMethods
useMutationEffect
useLayoutEffect
自定义hook
这是hook唯一可以写在component函数之外的地方
写个简单的例子
function useNumberInput(init) {
const [value, setValue] = useState(init);
const onInput = e => {
const val = +e.target.value;
if (Number.isNaN(val)) return;
setValue(val);
}
return { value, onInput };
}
function App() {
const foo = useNumberInput(123);
return (
<>
<div><input {...foo} /></div>
<p>value={foo.value}</p>
<p>typeof value={typeof foo.value}</p>
</>
);
}
用非常简单的方式实现了一个只能输数字的输入框,并且输入的内容会自动转换为Number类型,而且useNumberInput可复用