React Hooks 是 React 16.8 新增的特性,丰富扩展了原有函数组件的功能,让函数组件也有了类组件的一些特性。
同时,React Hooks解决了以下问题:
1、useState 是用来解决函数组件中不能定义自己的状态的问题,useState 可以传递一个参数,做为状态的初始值
,返回一个数组,数组的第一个元素是返回的状态变量
,第二个是修改状态变量的函数
。
他的定义格式,第一个是变量名,第二个是改变变量的方法
const [state, setState] = useState(initalState); // 初始化,state可以任意命名
// ...
setState(newState); // 修改state的值
在类组件
中是通过 this.setState
来修改类组件中的状态值的,函数组件
中则通过 useState
来修改
// 代码示例
import { useState } from 'react';
function Demo() {
const [count, setCount] = useState(0);
const add = () => {
setCount(count + 1);
};
return (
{`count: ${count}`}
);
}
export default Demo;
注意:在类组件中 this.setState 是异步
执行的,同样 useState 修改状态也是异步
的。也就是每次修改状态不是立马生效
的。那如何在每次修改状态后可以拿到最新的数据呢?就可以用我们下面讲的useEffect
实现。
2、useEffect 在类组件
中放在 componentDidMount,componentDidUpdate 等执行的请求获取数据的操作,在React Hooks中都可以用 useEffect
来处理,他可以类比类组件中的两个生命周期函数。
useEffect(() => {
// 此处编写 组件挂载之后和组件重新渲染之后执行的代码
...
return () => {
// 此处编写 组件即将被卸载前执行的代码
...
}
}, [dep1, dep2 ...]); // 依赖数组
useEffect 可以传入2个参数,第1个参数为我们定义的执行函数,第2个参数是依赖关系(可选参数)。若一个函数组件中定义了多个useEffect,那么他们实际执行顺序是按照在代码中定义的先后顺序来执行的。
// 代码示例
import { useState, useEffect } from 'react';
function Demo() {
const [a, setA] = useState(0);
useEffect(() => {
// 点击一次按钮就会触发一次useEffect
console.log("执行了useEffect");
return () => {
console.log("组件卸载");
}
}, [a]);
const add = () => {
setA(a + 1);
};
return (
{`a: ${a}`}
);
}
export default Demo;
3、useLayoutEffect
useLayoutEffect 使用方法、所传参数和 useEffect 完全相同。大多数情况下将 useEffect 替换成 useLayoutEffect 完全看不出区别。
唯一区别就是:使用 useEffect 时,页面挂载会出现闪烁。而使用 useLayoutEffect 时页面没有闪烁,是因为 useEffect 是在页面渲染完成后再去更新数据的,所以会出现短暂的闪烁,而 useLayoutEffect 是在页面还没有渲染时就将数据给更新了,所以没有出现闪烁。
注意:大部分情况用useEffect就足够了,useLayoutEffect 会阻塞渲染,所以需要小心的使用。
4、useMemo
useMemo 是为了减少组件重新渲染时不必要的函数计算,可以用来做性能优化
。
类似于vue中的计算属性,他的依赖值是后面数组中的值
const memoizedValue = useMemo(() => {
// 计算逻辑
...
// return res;
}, [a, b]);
useMemo 可以传入2个参数,第1个参数为函数,用来进行一些计算,第2个参数是依赖关系(可选参数),返回值为第一个函数 return 出去的值,只有在依赖项发生变化时才会重新执行计算函数进行计算,如果不传依赖项,每次组件渲染都会重新进行计算。
// 代码示例
import { useState, useMemo } from 'react'
function Demo() {
const [num, setNum] = useState(0);
const addNum = () => {
setNum(num + 100);
};
const total = useMemo(() => {
console.log('---求和---');
// 求和计算
let temp = 0;
for(let i = num; i > 0; i--) {
temp += i;
}
return temp;
}, [num]);
return (
{`num: ${num}`}
{`total: ${total}`}
)
}
export default Demo;
点击修改num的值,total 对应的计算函数会重新执行一遍,因为num是该计算函数的依赖项
。
5、useContext
在 React 中传递属性只能一层一层传,如果组件结构比较复杂,层级比较深的时候,数据传递起来就比较麻烦,可能会经过很多次的传递才能将属性传递到目标组件中,那么有没有一种可以在全局进行状态共享的实现方法呢?useContext 就是为了解决这个问题的,可以实现不必层层传递就能共享状态的功能。具体用法看下面步骤:
先封装一个js,里面可以设置初始值,这个初始值,可以在任何地方使用
import React from 'react';
// React.createContext()中的参数是默认值,可填可不填
const UserContext = React.createContext( { name: '张三' });
export default UserContext;
在代码中引用封装好的js文件
import React, { useContext } from 'react'
import UserContext from './context';
// const UserContext = React.createContext();
function Demo() {
// 如果React.createContext没有指定默认值,也可以在对应子组件上套上UserContext.Provider来指定值
return (
//
//
)
}
function Child() {
const user = useContext(UserContext);
return (
{`name: ${user.name}`}
)
}
export default Demo;
6、useReducer
useReducer 也是用来实现状态管理
的 hook,useState 就是基于 useReducer 实现的,useReducer 可以实现比 useState 更复杂
的状态管理逻辑。
它可以对多个值进行管理,他的定义方式和useState很像,但是,useState 是基于它实现的,使用时注意定义的参数
// 代码示例
import React, { useReducer } from 'react'
// 1.需要有一个 reducer 函数,第一个参数为之前的状态,第二个参数为行为信息
function reducer(state, action) {
switch (action) {
case 'add':
return state + 1;
case 'minus':
return state - 1;
default:
return 0;
}
}
function Demo() {
// 2.引入useReducer,第一个参数时上面定义的reducer,第二个参数时初始值
// 3.返回为一个数组,第一项为状态值,第二项为一个 dispatch 函数,用来修改状态值
const [count, dispatch] = useReducer(reducer, 0);
return (
{`count: ${count}`}
);
}
export default Demo;
7、useRef
useRef 可以帮助我们获取 dom 和 react 组件实例
,类组件中的 React.createRef()
也有相同的功能。
const xxxRef = useRef(initialValue);
// 使用 xxxRef.current 获取引用的值
// 代码示例
import { useRef } from 'react'
function Demo() {
const inputRef = useRef();
const handleFocus = () => {
// document.getElementById('my-input').focus();
inputRef.current.value = 'focus';
inputRef.current.focus();
}
const handleBlur = () => {
// document.getElementById('my-input').blur();
inputRef.current.value = 'blur';
inputRef.current.blur();
}
return (
)
}
export default Demo;
除了用 useRef 获取组件实例,还可以用来存储变量的值,但是需要注意的一点是,修改 .current 的值不会触发组件的重新渲染,请看下面示例:
import { useState, useRef } from 'react'
function Demo() {
const countRef = useRef(0);
const [num, setNum] = useState(0);
const addCount = () => {
// 使用 useRef 去更新值并不会出发组件渲染
countRef.current = countRef.current + 1;
}
const addNum = () => {
// 使用 useState 去更新会触发组件渲染
setNum(num + 1);
}
return (
{`count: ${countRef.current}`}
{`num: ${num}`}
)
}
export default Demo
8、useCallback
返回一个缓存的回调函数,缓存一个函数类型因变量
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b]
);
useCallback 的用法和 useMemo 完全一样,useMemo 返回的是计算函数 return 出去的值
,而 useCallback 可以理解成返回的是那个计算函数
。
// 代码示例
import { useState, useCallback } from 'react'
function Demo() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const funcHang = useCallback(function() {
console.log("function run:", count1, count2);
}, [count1]);
return (
params: { count1 } { count2 }
)
}
export default Demo;
9、React.memo
function Demo(props) {
// ...
}
function compare(prevProps, nextProps) {
// 自己写对比逻辑,返回 true 更新,false 跳过更新
// return false
}
export default React.memo(Demo, compare)
如果是引用类型,可以用下面方法进行比较
// 使用lodash库来完成对象的值的比较,从而用来完成减少组件的无用的重复渲染
(prevProps, nextProps) => _.isEqual(prevProps, nextProps)
// 代码示例
import { useState, useCallback } from 'react'
import * as React from "react";
import _ from 'lodash'
const Child = React.memo(
({ count }) => {
console.log('child');
return (
child组件 -- {count.n}
)
},
// 使用lodash库来完成对象的值的比较,从而用来完成减少组件的无用的重复渲染
(prevProps, nextProps) => _.isEqual(prevProps, nextProps)
);
function Demo() {
let [count, setCount] = useState({ n: 100 });
let [name, setName] = useState('张三');
return (
App -- {count.n}
setName(e.target.value)} />
)
}
export default Demo;
上面代码中,每次点击添加按钮,子组件props传来的数据发生变化,导致子组件进行重新渲染。而输入框输入内容,子组件props传来的数据未发生变化,子组件不会重新渲染。
10、forwardRef
forwardRef 可以在父组件中操作子组件的 ref 对象
,并且将 ref 对象作为一个参数
传递给了子组件。
// 代码示例
import { useRef, forwardRef } from 'react';
function Child(props, ref) {
return (
);
}
function Demo() {
const childRef = useRef();
const onFocus = () => {
childRef.current.focus();
}
return (
);
}
Child = forwardRef(Child);
export default Demo;
这行代码是关键,加上后子组件就多了个入参ref,来绑定对应子组件的dom元素。
Child = forwardRef(Child);
可以将他们分为两个部分(其中useRef 不属于两者):
自变量:本身是可以改变从而影响其他
因变量:因自变量改变而导致自己改变
自变量:
useState 定义自变量
useReducer 用于操作多个自变量
(一个层级的自变量和因变量也可以成为其他层级的自变量和因变量)
useContext 可以跨层级的传递自变量
因变量:
useMemo 和 useCallback(缓存函数类型因变量) 定义无副作用的因变量
useEffect 定义有副作用(可以进行一些操作,比如修改某些参数,请求数据,操作dom等)的因变量
useReducer 可看成是一个进阶版的useState ,借用redux的设计理念,将多个state合并成一个,本质上也是一个因变量。
useRef 可以操作子组件中的方法,也可以作为一个保存数值的标记,在路径中起到缓存数据的作用
文章借鉴:React Hooks中常用Hooks的用法详解_react常用的hooks_这里是杨杨吖的博客-CSDN博客