Hooks 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
简言之,使用 Hooks + Function Component 取代 Class Component
why Hooks
React 提供了 Class Component 和 Function Component 两种写法。Hooks 产生之前,需要 state
的时候,使用 Class Component;不需要 state
时,两种写法都可以使用。然而存在以下问题:
Function Component 重构成 Class Component 具有一定的复杂度
由于 Function Component 只是单纯的接收 props
、绑定事件、返回jsx
,本身无状态的组件。随着业务的发展 Function Component 不能满足需求的时候,需要将其重构成 class component,这其中存在一定的复杂度。例如:
Function Component :
function Hello(props) {
return (<div>Hello World</div>);
}
Class Component:
class Hello extends React.Component {
constructor(props) {
super(props);
// ...
}
render() {
return (<div>Hello World</div>);
}
}
Class Component 难以理解
先看一个完整的例子:
class Hello extends React.component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
// ...
}
componentWillMount() {
// ...
}
componentDidMount() {
// ...
}
componentShouldUpdate() {
// ...
}
componentWillUnMount() {
// ...
}
handleClick() {
// ...
}
render() {
return (<div onClick={this.handleClick}>Hello World</div>);
}
}
在这个例子中,可以看到各种生命周期的方法、this
、bind
。 this
指向困扰很多人。其次,各种生命周期方法,会使得组件很臃肿,难以理解
组件间复用状态逻辑
React 没有好的方法将可复用性逻辑“附加”到组件上(例如:把 store “附加”到组件)。但是我们可以使用 render props
和 HOC
。在使用 render props
和 HOC
的时候,你会发现,组件树的层级很深,容易陷入嵌套地狱。
例如:
// 组件
class Hello extends React.component {}
// 使用 HOC 添加某些属性
const NewHello = simpleHOC(Hello);
这时,Hello
的层级会变成:
- simpleHoc wrapper
- Hello
而 Hooks 的存在,让你很方便抽取可复用的逻辑,在组件中即可使用,无需额外的组件结构。
内置Hooks
我们先来看一个例子:显示计数器,点击按钮后,计数器的值 +1
function App() {
// 通过`useState` 定义了 `count` 变量,获得 `getter`、 `setter`
const [count, setCount] = useState(0);
// 其他 state
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
由上面的例子可以看出,使用 Hooks
可以把 UI 和逻辑分离
此外 react 内置 hooks 如下:
- 基础 Hooks
- useState:为组件添加 state
- useEffect:委组建添加副作用,render 之后生效执行
- useContext:跨组件共享数据
- 额外的 Hooks
- useReducer:管理复杂的数据结构
- useCallback
- useMemo:用于缓存耗时的计算结果
- useRef:返回一个 ref 对象,该 ref 在组件的整个生命周期内保持一致
- useImperativeHandle:配合 forwardRef 使用,用于自定义通过 ref 给父组件暴露的值
- useLayoutEffect:在浏览器绘制节点前执行
- useDebugValue:开发者工具调试
自定义 Hooks
以上内置 Hooks 还可以封装起来,变成一个自定义的 Hooks
例如:能够根据 id 获取数据
function useFetchData(id) {
const [data, setDate] = useState({});
useEffect(() => {
fetch(`https://xxxx/${id}`);
}, [id]);
return { data };
}
如何模拟生命周期
-
constructor
函数组件不需要构造函数。可以通过 useState 初始化 state
const [count, setCount] = use State(0);
-
componentDidMount
useEffect 传入第二个参数为 []
// ... useEffect(() => { // 需要在 componentDidMount 中执行的 }, []); // ...
-
componentDidUpdate
useEffect 传入第二个参数为空或者值变动的数组
// ... useEffect(() => { // ... }, [count]); // 仅在 count 值变化时更新 // ...
-
componentWillUnmount
useEffect 函数 return 一个函数来模拟
useEffect(() => { return function cleanup() { // 需要在 componentWillUnmount 中执行 } });
-
shouldComponentUpdate
使用 React.memo 包裹一个组件来对它的 props 进行浅比较
const Hello = React.memo((props) => { // 组件 });
- componentDidCatch 和 getDerivedStateFromError:目前还未支持
网友评论