State Hook
State Hook是一个在函数组件中使用的函数(useState), 用于在函数组件中使用状态
useState
- 函数有一个参数, 这个参数的值表示状态的默认值
- 函数的返回值是一个数组, 该数组一定包含两项
- 第一项: 当前状态的值
- 第二项: 改变状态的函数
一个函数组件中可以有多个状态, 这种做法非常有利于横切关注点.
注意的细节
- useState最好写到函数组件的起始位置, 便于阅读
- useState严禁出现在代码块(判断, 循环)中
- useState返回的函数(数组第二项), 引用不变(节约内存空间)
- 使用函数改变数据, 若数据和之前的数据完全相等(使用Object.is比较), 不会导致重新渲染, 以达到优化效率的目的
- 使用函数改变数据, 传入的值不会和原来的数据进行合并, 而是直接替换
- 如果要实现组件强制刷新
- 类组件: 使用forceUpdate函数
- 函数组件: 使用一个空对象的useState
- 如果某些状态之间没有必然的联系, 应该分化为不同的状态, 而不要合并成一个对象
- 和类组件的状态一样, 函数组件中改变状态可能是异步的(在DOM事件中), 多个状态变化会合并以提高效率, 此时不能信任之前的状态, 而应该使用回调函数的方式改变状态. 如果状态的变化要用到之前的状态, 尽量传递函数.
- 如果想要组件强制刷新, 可以使用一个空对象的useState
const [, foeceUpdate] = useState({});
Effect Hook
Effect Hook: 用于在函数组件中处理副作用
副作用:
- ajax请求
- 计时器
- 其他异步操作
- 更改真实的DOM对象
- 本地存储
- 其他会对外部产生影响的操作
函数: useEffect, 该函数接受一个函数作为参数, 接收的函数就是需要进行副作用操作的函数.
细节
- 副作用函数的运行时间点, 是在页面完成真实的UI渲染之后. 因此它的执行是异步的, 并且不会阻塞浏览器
- 与类组件中componentDidMount和componentDidUpdate的区别
- componentDidMount和componentDidUpdate, 更改了真实DOM, 但是用户还没有看到UI更新, 同步的.
- useEffect中的副作用函数, 更改了真实DOM, 并且用户已经看到了UI更新, 异步的.
- 每个函数组中, 可以多次使用useEffect, 但不要放入判断或循环等代码块中.
- useEffect中的副作用函数, 可以有返回值, 返回值必须是一个函数, 该函数叫做清理函数
- 该函数运行时间点, 在每次运行副作用函数之前
- 首次渲染组件, 不会运行
- 组件被销毁时一定会运行
- useEffect函数可以传递第二个参数
- 第二个参数是一个数组
- 数组中记录该副作用的依赖数据
- 当组件重新渲染后, 只有依赖数据与上一次不一样时, 才会执行副作用
- 所以, 当传递了依赖数据之后, 如果数据没有发生变化
- 副作用函数仅在第一次渲染后运行
- 清理函数仅在卸载组件后运行
- 副作用函数中, 如果使用了函数上下文中的变量, 则由于闭包的影响, 会导致副作用函数中的变量不会实时变化.
- 副作用函数在每次注册时, 会覆盖掉之前的副作用函数, 因此, 尽量保持副作用函数稳定, 否则控制起来会比较复杂
自定义Hook
自定义Hook: 将一些常用的, 跨越多个组件的Hook功能抽离出去形成一个函数, 该函数就是自定义Hook. 自定义Hook, 由于其内部使用Hook功能, 所以它本身也需要按照Hook的规则实现:
- 函数名必须以use开头
- 调用自定义Hook函数时, 必须放到代码的最前面
import { useEffect, useState } from 'react';
import { getAllStudents } from '../services/student';
export default function useAllStudents() {
const [students, setStudents] = useState([]);
useEffect(() => {
(async function () {
const stus = await getAllStudents();
setStudents(stus);
})();
}, []);
return students;
}
// 应用
import React from 'react'
import useAllStudents from './myHooks/useAllStudents';
function Test() {
const stus = useAllStudents();
const list = stus.map(it => {
return <li key={it.id}>{it.name}</li>
});
return (
<ul>
{list}
</ul>
)
}
export default function App() {
return (
<div>
<Test />
</div>
);
}
Reducer Hook
通用的useReducer函数(实际上官方已经有useReduce实现)
import { useState } from 'react';
/**
* 通用的useReducer函数
* @param {function} reducer reducer函数, 标准格式
* @param {any} initialState 初始状态
* @param {function} func 第三个参数是个函数, 计算初始值. 如果有第三个参数, 则第二个参数会当成第三个函数参数的参数传入
*/
export default function useReducer(reducer, initialState, func) {
const [state, setState] = useState(func ? func(initialState) : initialState);
function dispatch(action) {
const newState = reducer(state, action);
setState(newState);
}
return [state, dispatch];
}
Context Hook
用于获取上下文数据
import React, { useContent } from 'react';
const ctx = React.createContext();
export default function App() {
return <div>
<ctx.Provider value="abc">
<Test />
</ctx.Provider>
</div>
}
// 以前的使用方式
function Test() {
return <ctx.Consumer>
{
value => {
return <h1>Test, 上下文的值: {value}</h1>
}
}
</ctx.Consumer>
}
// 现在的使用方式
function Test() {
const value = useContent(ctx);
return <h1>Test, 上下文的值: {value}</h1>
}
Callback Hook
函数名: useCallback
用于得到一个固定引用值的函数, 通常用它进行性能优化
useCallback
该函数有两个参数:
- 函数, useCallback会固定该函数的引用, 只要依赖项没有发生变化, 则始终返回之前函数的地址
- 数组, 记录依赖项
该函数返回: 引用相对固定的函数地址
import React, { useCallback } from 'react';
class Test extends React.PureComponent {
render() {
return <div>
<h1>{this.props.text}</h1>
<button onClick={this.props.onClick}>改变文本</button>
</div>
}
}
export default function App() {
const [txt, setTxt] = useState(123);
const handleClick = useCallback(() => {
setTxt(Math.random());
}, []);
return (
<div>
{/* 函数的地址每次渲染都发生变化, 导致了子组件跟着重新渲染. 若子组件是经过优化的, 则子组件的优化可能就失效 */}
{/* <Test text={txt} onClick={() => {
setTxt(Math.random());
}} /> */}
<Test text={txt} onClick={handleClick} />
</div>
);
}
Memo Hook
用于保持那些比较稳定的数据, 通常用于性能优化.
它和useCallback的区别是:
- useCallback必须传入一个函数, 它固定的就是这个传入的函数
- useMemo传入一个函数, 但是它固定的是这个函数的返回值, 可以是任意类型
如果React元素本身没有发生变化, 它一定不会重新渲染
import React, { useCallback, useMemo } from 'react';
// 和useCallback的区别
const handleClick = useCallback(() => {
setTxt(Math.random());
}, []);
const fixedData = useMemo(() => {
// 函数的返回结果, 给固定下来, 可以返回任何东西
return () => { console.log('xxx') }
}, []);
// 使用
export default function App() {
const [range, setRange] = useState({ min: 1, max: 10000 });
const [n, setN] = useState(0);
const list = useMemo(() => {
const lis = [];
for (let i = range.min; i <= range.max; i++) {
console.log(i);
lis.push(<li key={i}>{i}</li>);
}
return lis;
}, [range.min, range.max]);
return (
<div>
<ul>
{list}
</ul>
<input type="number"
value={n}
onChange={e => {
setN(parseInt(e.target.value));
}} />
</div>
);
}
Ref Hook
useRef函数
- 一个参数, 默认值
- 返回一个固定的对象,
{current: 值}
import React, { useRef } from 'react';
export default function App() {
// 以前的ref创建和使用方式
// 每次App组件刷新, 都会重新生成一个新的ref
const inpRef = React.createRef();
// 新的useRef方式
const newRef = useRef();
return (
<div>
<input type="text" ref={inpRef} />
<button onClick={() => {
console.log(inpRef.current.value);
}}>得到input的值</button>
</div>
);
}
注意
useRef(initialData) 也可以用来在函数组件中固定数据/对象, 即函数组件多次刷新渲染, 不会导致该对象重新创建, 而是使用和第一次创建时相同的引用的同一个对象!!!
ImperativeHandle Hook
函数: useImperativeHandle
- 参数1: ref, 函数组件在forwardRef后, 传入的第二个参数ref
- 参数2: function, 如果不给依赖项, 函数组件每次运行都会调用这个函数, 它的返回结果将附着在ref.current属性上
- 参数3: 数组, 依赖项
function Test(props, ref) {
useImperativeHandle(
ref,
() => {
// 如果不给依赖项, 则每次运行函数组件都会调用
// 如果使用了依赖项, 则第一次调用后会进行缓存, 只有依赖项发生变化时才会调用
// 函数的返回值, 附着到ref的current属性上
return {
method() {
console.log('Test Component called');
}
}
},
[]
)
return <h1>Test Component</h1>
}
const TestWrapper = React.forwardRef(Test);
export default function App() {
const testRef = useRef();
return (
<div>
<TestWrapper ref={testRef} />
<button onClick={() => {
testRef.current.method();
}}>点击调用Test组件的method方法</button>
</div>
);
}
LayoutEffect Hook
useEffect: 浏览器渲染完成后, 用户看到效果之后
useLayoutEffect: 完成了DOM改动, 但浏览器还没有重新渲染呈现给用户
应该尽量使用useEffe, 因为它不会导致渲染阻塞(因为浏览器已经完成了渲染, 而如果使用useLayoutEffect并且处理内容非常多, 会阻塞浏览器渲染). 如果出现问题, 再考虑使用useLayoutEffect.
QQ浏览器截图20200503163021.pngDebugValue Hook
useDebugValue: 用于将自定义hook的关联数据显示到调试栏
如果创建的自定义Hook通用性比较高, 可以选择使用useDebugValue方便调试.
import React, { useDebugValue, useState } from 'react';
function useTest() {
const [students, ] = useState([]);
// 传入的值会在调试工具栏中的hooks处显示
useDebugValue(students);
return students;
}
export default function App() {
useState(0);
useState('abc');
useTest();
useEffect(() => {
console.log("effect");
return () => {
}
}, [])
return (
<div></div>
);
}
QQ浏览器截图20200503165218.png
网友评论