hooks学习

Thinking In Life Why We are Living.

Hooks是React 16.8新加特性,这篇博客记录于v16.9.0。不能在class组件里使用Hooks,但是你可以通过使用state和React的一些特性而不用写class。

何时使用

当你写函数组件时,发现自己需要使用state的时候。

useState

无Hooks的无状态组件

1
2
3
const Example = (props) => {
return <div />;
}

Hooks使你可以实现可复用的状态组件

1
2
3
4
5
import React, { useState } from 'react';

function Example() {
const [count, setCount] = useState(0);
}

useState(initial)只接受一个参数,即count的初始值。

返回值是一个包含当前组件的state和一个更新它的函数的数组,然后利用解构赋值赋值给我们定义的变量名(名字我们自己起的)。

如果需要存储多个值在state里,需要调用useState()多次。

count是我们定义的state属性,可以是任何名字。类似于class组件里的this.state.count

setCount是用来更新count的,可以是任何名字。类似于class组件里的this.setState()

定义多个

1
2
3
4
function ExampleWithManyStates() {
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

useEffect

类似于class组件中componentDidMountcomponentDidUpdate

分两种情况来学习useEffect

(1)第一种是effect不需要消除

有的时候我们希望产生同一个effect(比如请求),不管是Didmount还是DidUpdate,如果是class组件,要在componentDidMountcomponentDidUpdate两个函数里写同样的代码,没有一个统一的方法。而Hooks提供了useEffect完成了这一需求。

1
2
3
useEffect(() => {
document.title = `You clicked ${count} times`;
});

类似于

1
2
3
4
5
6
7
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}

componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}

这里要注意,函数式组件和class组件渲染的不同,函数组件是调用函数(相当于class组件中的render()函数)所以每次更新函数式组件都要重新调用组件函数,而class组件一直存在内存里(只要不卸载它)(new的一个对象),每次更新只要调用render()就行了。

解释

默认,每次更新或者第一次render都会调用useEffect里的回调。

因为在同一个函数作用域链里,所以,useEffect能直接访问useState()里的变量。

(2)还有一种是需要在组件卸载的时候清除effect

class组件的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}

componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}

Hooks的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}

ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
//return () => {
//ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
//}
});

解释

cleanup函数名并不是闭包必须的,可以返回一个匿名/箭头函数。

cleanup执行时机:每次组件卸载(unmount)都会执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run first effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // Run next effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run next effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect

useEffect的第二个参数

class组件写法

1
2
3
4
5
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}

hooks写法

1
2
3
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

两者效果是一样的。

注意

数组里的比较使用 === 运算符,只要数组一项改变,就会重新执行useEffect()(对有cleanupuseEffect也同样有效)

如果你只需要在mountunmount时只执行一次useEffect,传入[]空数组。

题外

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。而且像importexport这两个命令现在在任何浏览器中都是不支持的, 同时babel也无法将其转换为浏览器支持的ES5。

原因在于:
babel只是个翻译,假设a.jsimportb.js, 对a.js进行转码,只是翻译了a.js,并不会把b.js的内容给读取合并进来, 如果想在最终的某一个js里,包含 a.jsb.js 的代码,那就需要用到打包工具。(哎,写个demo都要用webpack的嘛)

配置webpack的一些坑

Error Decoder

Target container is not a DOM element.

如果要手动引入标签,要把bundle.js放在ReactDOM.render(<MyComponent />, DOMContainer);后面。

打包的时候出现找不到react、react-dom模块

1
2
$ npm install --save react
$ npm install --save react-dom
-------------要说再见啦感谢大佬的光临~-------------