简单说,hook就是一些函数,不能在Class组件中使用,只能在函数组件中使用。不使用class组件,而全部使用函数组件是一个趋势
一、Hook 简介
Hook 是 React 16.8 的新增特性。要启用 Hook,所有 React 相关的 package 都必须升级到 16.8.0 或更高版本。如果你忘记更新诸如 React DOM 之类的 package,Hook 将无法运行。
它可以在使用函数组件的时候使用this(state就在this里面)和生命周期等,其它的类组件才用的特性。
如下,为使用第一个名字叫useState的hook
import React, { useState } from 'react';
function Test(props) {
// 声明一个新的叫做 “count” 的 state 变量
const [count, setCount] = useState(2); // 这个useState里面的参数,就是count的值
return (
<div>
<p>You clicked {count} times</p> // 这一行显示在界面上为 You clicked 2 times
</div>
);
}
export default Test;
Hook 不会影响你对 React 概念的理解。 恰恰相反,Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期
- 使用hooks的原因
- 在组件之间复用代码很难:即 用hook的解决方法为自定义hook(就是自定义一个hook样式的全局函数)。一般的解决办法就是render props 和 高阶组件等,但是由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”,因为它会一层一层的嵌套传值。已经深入感受到了,确实很麻烦
- 复杂组件变得难以理解:即组件中到处都在用setState,导致很乱,用hook的解决办法为useState函数和useEffect函数。例如,组件常常在 componentDidMount 和 componentDidUpdate 中发送同样的ajax请求,就造成了重复的代码,然后就很乱
- 难以理解的 class: 即react中的class组件很难理解。class 不能很好的压缩,并且会使热重载出现不稳定的情况。因此,我们想提供一个使代码更易于优化的 API。
hook有一个特点,就是即使在同一个函数组件中,多次调用同一个hook,它们内部的值是完全独立的。hook是使用了js的闭包的特性
二、使用 State Hook
state hook也可以叫做useState hook
如果不用useState hook,而是直接在react中定义一个变量,那么修改这个变量的时候,变量的值是会改变,但是不会触发dom的更新。而useState是会同时修改这个变量,并且触发dom更新的
使用State Hook需要注意的一点是,不必把每个需要存放在state里面的变量都调用useState来单独创建,由于State 变量可以很好地存储对象和数组;所以,不必使用多个 state 变量,只需要把多个有关联的数据弄成一个对象,然后创建一个state就好了。这里又有一个注意的是,State Hook更新状态的时候总是替换它,而不是像class的this.state那样合并它。所以必须确保,每次更新state hook的数据的时候是更新对应对象里面的全部值
- useState hook函数: 这个函数就是一个用来在函数组件中,创建state的 Hook函数
- <0>引入:使用hook之前需要先在函数组件中引入。
import React, { useState } from 'react';
。逗号前面为默认导入,后面为命名空间导入- <1>调用:useState在被调用的时候,会创建一个“state 变量”,这个变量可以叫任何名字。然后作为返回值返回出去
- <2>参数:useState只接收一个参数,为返回的state变量的初始值(这个参数就可以不是对象了,可以是任意类型)。
- 这个创建初始值只有在函数组件第一次render的时候会被用到。后面比如更新之前的render,都是直接返回给我们当前的state值,而不需要再用这个初始值了
- 如果想要在 state 中存储两个不同的变量,只需调用 useState() 两次即可
- <3>返回值:useState 会返回一个有两项的数组:
- 第一项为返回的state变量的名字(React 会在重复render时保留这个 state名字,并且提供最新的值给函数组件)。类似于class组件上面的this.state上面的变量值,如
this.state.count
- 第二项为用于更新这个state的函数。更新函数类似于 class 组件的 this.setState。依然有两个作用,第一个作用为改变state的值,第二个作用为触发dom更新
- 这个更新函数只能在函数组件的最外层调用,不要在循环、条件判断或者子函数中调用
- 更新函数也接收一个参数,这个参数会被直接赋值(this.setState是把新旧state合并,而这个更新函数相当于是直接替换) 给对应的state变量。比如,
const [count, setCount] = useState(0);setCount(10)
,后面就会把传入的10赋值给count- 如果调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行
- 如果想要像class组件那样,执行了setState之后,执行一个回调。useState hook是没有提供这个功能的,这就需要使用useEffect hook了,监听对应的state变化,然后执行回调
- 注:如果想要直接改变state属性的值,而不用useState返回的更新函数,和class组件一样,依然是可以改变值state属性的值的,但是不能触发dom更新
import React, { useState } from 'react'; // 引入hook;使用hook之前需要先引入
function Example() {
const [count, setCount] = useState(0); // 数组解构赋值,来获取useState返回的两个值。创建的state变量,被命名为count
// 并且state 变量可以声明多个
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks',go: '11' }]); // 这一个todos就是一个数组,数组里面是对象
return (
<div>
<p>You clicked {count} times</p> // 读取state里面值,直接用变量名字就好了
<button onClick={() => setCount(count + 1)}> // 这里就是调用更新函数,来更新state。更新函数中接收的参数,会被赋值给对应的state变量
Click me
</button>
</div>
);
}
// 下面为直接改变state属性的值。这样依然可以,但是不能触发dom更新
let [count, setCount] = useState(1000);
const func = () =>{
console.log(count); // 第一次进入时,count值为1000;第二次进入这个函数的时候,count值为20
count = 20;
};
return <div>{count}</count>; // 这里的count一直都是1000,不会被改变。所以上面那样子直接改变值,不会触发dom更新
三、使用 Effect Hook
- useEffect hook函数: 这个函数就是一个在函数组件中,来使用class组件的声明周期的hook函数。
- <-1>注:可以把 useEffect Hook 看做是,class组件中的 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个声生命周期函数的组合。
- componentDidMount 和componentDidUpdate调用时机:是在render之后同步的调用,即页面要等到这里面的代码全部执行完了之后才显示出来,所以如果这里面的代码执行的特别久,就有可能会阻塞页面,导致页面很久都不出来。useLayoutEffect这个hook的调用时机就和前面函数一样的,都是同步
- useEffect调用时机:这个也是在render之后调用,不过就是异步调用了,是在页面更新完成了之后,也就是说页面已经出现了之后,再执行里面的callback函数,所以不会阻塞页面;
- <0>引入:使用hook之前需要先在函数组件中引入。
import React, { useEffect } from 'react';
。逗号前面为默认导入,后面为命名空间导入- <1>调用:react会在每次组件执行了render(即dom渲染完成了)之后(包括第一次mounted时候的render),才异步调用useEffect里面的callback函数。
- (1)调用useEffect函数的时候,会根据useEffect传入的第二个参数(不传入的话会执行第二个参数),来决定是否调用它的第一个函数参数
- (2)按照代码的用途,分离使用多个hook:可以使用多个useEffect hook函数,每个函数中只关注相同用途的代码;把不同用途的代码分在多个hook里面
- <2>参数:useEffect hook有两个参数,第一个参数为一个函数,第二个参数为一个state更新数组。
- (1)第一个函数参数:这个函数参数就是在useEffect hook在调用的时候,同时被调用的。可以没有返回值,也可以返回一个清除函数
- 返回一个清除函数:这是 effect 可选的清除机制,实际调用的时机好像和官网说的有点不一样。
- 调用时机:这里需要强调的就是,这个清除函数类似class组件的componentWillUnmount函数。但是componentWillUnmount函数只在组件卸载(销毁)(这个即通过切换路由来切换了组件)的时候才调用。 而useEffect hook的清除函数在组件销毁和、组件更新之前都会被调用。如果要实现清除函数只在组件销毁的时候才调用,还是就把第二个参数传空数组就好了。
useEffct(()=>{return ()=>{}},[])
,这样卸载函数就只会在组件卸载的时候才调用- (2)第二个state更新数组参数:这个参数是可选的。
- 数组里面的项是一些state的变量名字。这个数组表示只有对应的state变量被更新的时候,才执行这个useEffect函数。比如,
useEffect(() => {}, [count]);
,这个就是只有count变量被更新的时候,才执行这个hook- 如果传入一个空数组,即只运行一次的 effect。如:
useEffect(() => {}, []);
,只在仅在组件挂载的时候执行,卸载的时候还是会执行卸载函数的- 这个数组的必要性是,如果不加这个数组,那么不管什么时候,只要有state变量更新了,那么都会执行这个useEffect,这肯定是不被希望的
- <3>返回值:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 相当于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 变量被更新的时候,才执行这个函数。初次挂载也算是变量被更新
const [isOnline, setIsOnline] = useState(null);
useEffect(() => { // 使用多个useEffect。按照代码的用途,把代码放在不同的useEffect里面
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => { // 这里是返回一个清除函数,在组件卸载或者更新之前都会调用这个函数
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
四、使用 useContext Hook
- useContext hook函数: 这个函数就是一个在函数组件中,来使用class组件的context的hook函数。
- <0>引入:使用hook之前需要先在函数组件中引入。
import React, { useContext } from 'react';
。逗号前面为默认导入,后面为命名空间导入- <1>调用:这个hook是在子组件中来调用的,相当于是替换class组件中的contextType(因为这个属性是无法在函数组件中使用的)或者Consumer组件。父组件依然是需要使用React.createContext和Provider组件的
- 调用了 useContext 的组件总会在 context 值变化时重新渲染
- <2>参数:useContext hook只接收一个参数,为React.createContext创建的context对象本身
- <3>返回值:返回传入的 context对象 的当前值。这个当前值,即距离当前组件最近的Provider组件的value属性
// Test.js
import React, {useRef, createContext } from 'react';
import { Button } from 'antd';
import Test1 from './test1';
export const MyContext = createContext(null); // 创建context。把这个context导出,因为是需要到目标文件种去使用的
export const MyContext2 = createContext(null);
function Test(props) {
const [count, setCount] = useState(0);
const [ go, setGo ] = useState(3333)
return (
<div>
show!!!
<Button onClick={()=>{setCount(count + 1)}}>click me?????</Button>
<MyContext.Provider value={count}>
<MyContext2.Provider value={go}>
<Test1 />
</MyContext2.Provider>
</MyContext.Provider>
</div>
);
}
// Test1.js
import React from 'react';
import Test2 from './test2';
function Test1(props) {return (<div><Test2 /></div>);}
// Test2.js
import React, {useContext } from 'react';
import {MyContext, MyContext2} from './Test'; // 必须要导入这个
function Test2(props, context) {
const myContext = useContext(MyContext); // 使用hook,接收的参数为对应的context对象。返回的值就是上面那个provide组件的value值
return (
<div>
<MyContext.Consumer> // consumer组件使用,没撒用
{(value) =>(<div>res: {value}</div>)}
</MyContext.Consumer>
<div>{myContext.map(value => <span value={value.goValue}>{value.goName}</span>)}</div> // 直接用 context 的值
</div>
);
}
五、Hook 规则
- Hook 使用规则: Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则
- 只能在函数最顶层进行Hook的初始化。不要在循环、条件判断或者子函数中调用
- 原因是,React 进行state和 useState的对应,靠的是 Hook 初始化顺序。如果,把其中一个hook的初始化放在条件语句中,则会产生bug
- 只能在 React 的函数组件和自定义的 Hook 中调用 Hook。不要在其他 JavaScript 函数中调用
// state和 useState的对应
// 首次渲染
let first = true;
let a = useState('1') // 第一个state变量渲染a的值是1
if (first === true) {
let b = useEffect('2') // 第二个state变量渲染由于if返回true,则b的值是2
}
let c = useState('3') // 第三个state变量渲染c的值是3
// 二次渲染
let first = false;
let a = useState('1') // 第一个state变量渲染a的值还是1
if (first === true) {
let b = useEffect('2') // 第二个state变量渲染由于if返回false,则这一行不执行
}
let c = useState('3') // 由于上面的state变量没渲染,所以这一个state变量实际上在这次的渲染过程中,是第二个state变量渲染。则c的值是2
六、使用 自定义 Hook
原因: 有时候我们会想要在组件之间重用一些状态逻辑(即复用一段代码)。目前为止,有两种主流方案来解决这个问题:高阶组件和 render props。自定义 Hook 可以让你在不增加组件的情况下达到同样的目的
由于使用很多的高阶组件会产生回调地狱,所以在复用一些简单的逻辑的时候尽量使用自定义hook,而在编写插件的时候,还是使用高阶组件。
- 定义:自定义hook简单说就是 自定义一个,名字以use开头的全局函数,用来在其它组件中调用。这个函数可以*决定需要的参数和它的返回值
- 使用:自定义hook依然是需要编写在一个文件里面,然后导出(一般就不默认导出了),最后在其它的组件中导入,再像普通的hook那样使用的
// 下面为自定义一个hook,函数的名字以use开头。并且需要导出,这个文件为hook.js
import React, {useState, useEffect } from 'react';
export const useRequest = friendID => {
const [isOnline, setIsOnline] = useState(1);
function handleStatusChange(status = 1) { // 内部闭包函数,用来改变自己这个自定义hook内部的值
setIsOnline(status + 1);
}
//return {isOnline, handleStatusChange};
return [isOnline, handleStatusChange];
};
// 下面为在函数组件中使用自定义hook。还是需要引入
import React, {PureComponent, useState, useEffect } from 'react';
import { useRequest } from './hook'; // 这里为引入自定义的hook
function Test(props) {
let [count, setCount] = useState(0);
// let {isOnline, handleStatusChange} = useRequest(1); // 使用自定义hook
let [isOnline, handleStatusChange] = useRequest(1); // 使用自定义hook
return (
<>
<div onClick={()=>{handleStatusChange(1);}}> // 使用自定义hook返回的闭包函数,来改变自定义hook里面的值
22233355!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</div>
</>
);
}
export default Test;
七、Hook API 索引
hook的调用时机
useState: setState
useReducer: setState
useRef: ref
useImperativeMethods: ref
useContext: context
useCallback: 可以对setState的优化
useMemo: useCallback的变形
useLayoutEffect: 类似componentDidMount/Update, componentWillUnmount
useEffect: 类似于setState(state, cb)中的cb,总是在整个更新周期的最后才执行
1、基础 Hook
- (1)useState:
const [state, setState] = useState(initialState);
- (2)useEffect:
useEffect(didUpdate);
- (3)useContext:
const value = useContext(MyContext);
2、额外的 Hook
- (1)useReducer函数: useState底层就是执行的这个useReducer,所以可以在任何需要使用useState的地方来使用useReducer。主要是用于state的值需要进行判断,然后再进行修改的时候,就用这个useReducer来替代useState。
- <1>参数:useReducer接收3个参数,
const [stateValue, setStateValueDispatch] = useReducer(reducer, initialState, initFunc);
- reducer: 这个参数为一个函数。函数的样子如
(initialState, action) => newState
。这个reducer的返回值,就是这个useReducer hook返回的state值
- initialState参数:这个参数,就是useReducer中传入的initValue。如果不是第一次调用,那么就是当前的state
- action参数:这个参数,就是改变函数,传入的值。
- initialState: 传入reducer的初始state值
- initFunc: 这个参数为一个函数。作用是,接收上面那个initialState作为参数,然后把返回值再传入reducer。这个参数是可以不传。
- <2>返回值:useReducer 依然会返回一个有两项的数组:
- 第一项依然为返回的state变量的名字,这个值就是reducer返回的值
- 第二项依然为用于更新这个state的函数,不过这个改变函数和useState的改变函数是不一样的,useState的更新函数传入的参数直接就替换state。而useReducer的更新函数传入的参数是reducer的第二个参数
- 上面说的useState底层就是执行的这个useReducer,简单说就是useState有个默认的reducer函数,这个函数里面,是直接把传入的antion直接返回出去,所以useState的更新函数传入的参数是直接替换state。其它都是一样的
const initialState = 0;
function initFunc(initialCount) { // 这个就是用来改变传入reducer的initialState的函数,这个函数不是必须的
return {count: initialCount};
}
function reducer(initialState, action) {
switch (action.type) {
case 'increment': return {count: initialState.count + 1};
case 'decrement': return {count: initialState.count - 1};
default: throw new Error();
}
}
function Counter() {
const [stateCount, setStateCountdispatch] = useReducer(reducer, initialState, initFunc);
return (
<>
Count: {stateCount.count}
<button onClick={() => setStateCountdispatch({type: 'decrement'})}>-</button> // 这里的setStateCountdispatch就是更新函数,传入的参数,就为reducer的第二个参数action
<button onClick={() => setStateCountdispatch({type: 'increment'})}>+</button>
</>
);
}
- (2)useCallback函数: 作用为 配合React.memo使用,如果传给子组件的有函数,就需要用这个hook来包裹这个函数,否则React.memo的优化失效的问题。原因为,组件刷新的时候函数会重新创建,所以传给子组件的函数就值向不同的地址。解决原理为使用useCallback包装之后会返回一个对应函数的memoized值,而这个useCallBack只会在首次渲染或者依赖项改变的时候才会被再次调用,所以memoized值就不一定改变了,所以传入子组件的就为同一个值
- <1>参数:useCallback接收2个参数,
const memoizedCallback = useCallback(callback,deps );
- callback: 这个参数就是需要传递给子组件的函数
- deps: 这个为一个依赖项数组,就像是useEffect的那个依赖项数组,包括组件里面的state和组件里面的props。仅当数组里面的值发生改变的时候,这个useCallback才会被再次调用。传入的函数里面的子函数如果有依赖项,那么这个依赖项也需要被传入当前的useCallback,不然子组件就访问不到值;传入的函数里面有用到antd的form包装的东西,也需要把form传进去,不然form相关的都无法改变。如果传入的数组为空,则就只有第一次渲染的时候会被调用
- <2>返回值:useCallback的返回值为,该回调函数的 memoized 版本,简单说就是 返回传入的callback函数,返回的callback函数也只会在依赖项数组改变的时候才改变
- 一般情况下,React.memo和useCallBack和useMemo要一起使用。React.memo相当于是全局的,减少整个子组件的重复渲染次数,而useCallBack是为了解决给子组件传入函数的时候,React.memo无效的问题;useMemo是为了解决给子组件传入对象的时候,React.memo无效
- 注:也可以不配合React.memo使用,单独包裹自己组件里面的函数,来让函数不会每次都重新创建
function Counter(props) {
const [ form ] = props;
const func1 = () => { // 每次刷新,这个函数都会重新创建,来指向不同的地址
console.log('show me');
};
const func2 = useCallback(() => {
count;
}, []);
const func3 = useCallback(() => {
count;
}, [aa,bb]); // 在aa或者bb改变的时候这个函数才重新创建指向不同的地址
const demo = useMemo(()=>{
return (
<Form>
<Form.Item>
{form.getFieldDecorator('brandValue',{initialValue: 'nissan',})(
<Select placeholder='全部' allowClear style={{width: '100%'}}>
{brandList.map(value => <Select.Option key={value.lookupValueCode} value={value.lookupValueCode}>{value.lookupValueName}</Select.Option>)}
</Select>
)}
</Form.Item>
</Form>
);
}, [form]); // 这里是需要把form传入进去的,不然上面的select框是无法进行修改的
return (
<>
<Test1 demoFunc={func1} /> // 这个方法传入,会让React.memo失效。因为函数组件每次刷新的时候,会把里面的东西全部重新创建,所以每次创建的函数,指向的地址都不一样了
<Test2 demoFunc={func2} /> // 这个方法传入,不会让React.memo失效
<Test3 demoFunc={func3} /> // 这个方法传入,不会让React.memo失效
<Test4 demoFunc={()=>{}} /> // 这个方法传入,也会让React.memo失效。因为创建匿名函数了,所以函数指向的地址不一样
</>
);
}
- (3)useMemo函数: 这个实际上是useCallback的变形,也是配合React.memo使用的,如果传给子组件的有 对象 ,那么就要用这个useMemo包裹这个对象,否则React.memo也会失效
- <1>参数:useMemo接收2个参数,
const memoizedValue = useMemo(callback,deps);
- callback: 这个参数就是,一个函数,这个函数需要有一个返回值(一般为一个对象)
- deps: 这个为一个依赖项数组,就是这个依赖数组里面的值改变了之后,useMemo钩子才会被重新调用。如果这个值为[],那么依然是只有第一次渲染的时候才会被调用
- <2>返回值:返回值,就是 返回传入的第一个callback函数里面返回值(一般为一个对象)
- 注:也可以不配合React.memo使用,单独包裹自己组件里面的对象,来让对象不会每次都重新创建
function Test (props) {
const [count, setCount] = useState(0);
const demo = useCallback(()=>1, []);
const obj1 = useMemo(()=>({a:11}),[]);
const obj2 = useMemo(()=>({a:11}),[count]);
return (
<div className="my-component" onClick={() => { setCount(count + 1) } }>
<Test1 obj={{a:1}} func={demo} /> // 这个会让React.memo失效
<Test2 obj={obj1} func={demo} /> // 不会让React.memo失效,只会在组件第一次加载的时候重新计算
<Test3 obj={obj2} func={demo} /> // 不会让React.memo失效,会在count刷新的时候重新计算
</div>
);
}
export default Test;
- (4)useRef:
const refContainer = useRef(initialValue);
- useRef 返回一个可变的 ref 对象,获取ref对应的dom依然是用ref的.current 属性。
- 当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。
- 注:如果是配合antd一起使用。并且是获取Modal组件里面的子元素的ref,那么必须给Modal组件设置属性getContainer为false(即让Modal组件挂载到当前dom)。然后才可以获取到Modal组件里面子元素的ref,不然的话只能获取到Modal的ref,是不能获取到Modal的子元素的ref的
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
let dev = inputEl.current; // 获取ref对应的dom元素
};
return (
<>
<input ref={inputEl} type="text" /> // React 会将 ref 对象的 .current 属性设置为这个绑定的 DOM 节点
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
- (5)useImperativeHandle:
useImperativeHandle(ref, createHandle, [deps])
。这个是在使用 ref 时自定义暴露给父组件的实例值。好像没啥用,没想到使用场景
+ componentDidMount 和componentDidUpdate调用时机:**是在render之后同步的调用,即页面要等到这里面的代码全部执行完了之后才显示出来**,所以如果这里面的代码执行的特别久,就有可能会阻塞页面,导致页面很久都不出来。useLayoutEffect这个hook的调用时机就和前面函数一样的,都是同步 + **useEffect调用时机**:这个也是在render之后调用,不过就是**异步调用了,是在页面更新完成了之后,也就是说页面已经出现了之后,再执行里面的callback函数**,所以不会阻塞页面;
- (6)useLayoutEffect:这个hook可以说和useEffect hook的参数和作用那些完全一致。不同点在于调用的时机不同,一般情况下都是用useEffect而不用这个的
- useLayoutEffect的回调调用时机为render之后同步调用。和class组件的componentDidMount、componentDidUpdate生命周期的调用时机一样。由于是render之后同步调用,所以要等代码完成执行完了之后才显示出页面,所以如果useLayoutEffect的回调特别耗时,就有可能导致页面出现卡顿
- useEffect的回调的调用时机为render之后异步调用。所以就和上面那些的行为有点不一致了。由于是render之后异步调用,所以可以在页面显示出来之后,才执行。所以不会造成页面的卡顿
// 实例
useEffect(() => { // 直接用这个的话,会先在页面的最左边出现box,然后很快box就移动到中间。所以,这个方法是显示了页面之后,再运行下面的代码
TweenMax.to(REl.current, 0, {x: 600}); // 假设这个代码为让页面中的一个box,移动页面中间,延迟时机为0秒
}, []);
useLayoutEffect(() => { // 用这个的话,不会看到box在左边。只会看到box在中间。所以,这个方法是下面的代码全部运行完了之后才显示页面
TweenMax.to(REl.current, 0, {x: 600}); // 假设这个代码为让页面中的一个box,移动页面中间,延迟时机为0秒
}, []);
- (7)useDebugValue:
useDebugValue(value)
。这个用于在 React 开发者工具中显示自定义 hook 的标签,好像没撒用