一、useState Hook
1. 什么是useState Hook
useState 是一个 React Hook,它允许你向组件添加一个 状态变量。
2. 使用
(1)引入并调用 useState(), 返回值包含 state数据 和 修改state数据的方法,参数是state数据的初始值。
const [myState, setMyState] = useState()
(2)调用 修改state数据的方法 对state进行修改,注意 函数组件内没有this。
写法一:直接将最新值作为参数传入 setState(最新值)
写法二:函数作为参数,接收旧值,手动返回新值 setState((oldValue)=>newValue)
1: import React, { useState } from 'react';
2:
3: function Example() {
4: const [count, setCount] = useState(100);
5:
6: return (
7: <div>
8: <p>You clicked {count} times</p>
9: <button onClick={() => setCount(count + 1)}>
10: Click me
11: </button>
12: </div>
13: );
14: }
二、useEffect Hook
1. 什么是useEffect Hook
useEffect 是一个 React Hook,它允许你 将组件与外部系统同步。(生命周期函数的另一种表现形式)
2. 使用
(1)引入并调用 useEffect,第一个参数是一个 回调函数,第二个参数是一个 数组。
useEffect(()=>{ },[])
(2)第一个参数相当于 生命周期钩子; 第二个参数相当于 监听某些state数据的变化。
(3)如果写了第二个参数,且数组中有值,则第一个函数会在 页面首次渲染、被监听的数据更新 时被调用。
(4)如果写了第二个参数,但是一个空数组,则第一个函数会在 页面首次渲染 时被调用。
(5)如果不写第二个参数,则第一个函数相当于componentDidMount、componentDidUpdate,在 页面首次渲染 后和 任意state数据更新 后都会执行。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 无需清除的副作用:DOM首次渲染完成 或 更新后,设置标题
useEffect(() => {
document.title = `你点击了 ${count} 次`;
});
// 无需清除的副作用:只在DOM首次渲染完成时执行
useEffect(()=>{
console.log('DOM首次渲染完成!')
},[])
return (
<div>
<button onClick={() => setCount(count + 1)}>点我加1</button>
</div>
);
}
(6)如果想要 清除副作用,在useEffect()的执行器函数中,返回一个函数,被返回的函数就相当于 componentWillUnmount,可以进行清除定时器等清除操作。
(将 useEffect() 放在 组件内部,让我们可以在 effect 中直接访问 state 变量 或其他 props)
handler=()=>{
console.log('浏览器大小发生了变化')
}
useEffect(() => {
window.addEventListener('resize',handler)
return () => {
window.removeEventListener('resize',handler)
};
});
三、useRef Hook
1. 什么是useRef Hook
useRef 是一个 React Hook,它能帮助我们获取组件信息,或者存储变量。
2. 用法一:获取子组件信息
(1)引入并调用 useRef
const myRef = useRef()
(2)给组件绑定ref(不能给函数组件直接绑定Ref)
<input ref={myRef} />
(3) 如果要给 函数组件 绑定ref,就要用到 和
。
① 父组件内,定义Ref变量,并给子组件 绑定Ref。
② 子组件内,用 forwardRef()包裹子组件,同时从参数接收 父组件传递过来的ref。ref有以下两种用法:
③ 如果将ref绑定到了子组件的 某个元素上,则父组件可以获取到这个元素的 DOM对象。
④ 如果使用 useImperativeHandle() 函数,则可以将 数据、方法 暴露给父组件。(第一个参数是 父组件传递过来的ref,第二个参数是一个回调函数。)
import {useRef} from 'react';
const Father= () => {
const SonRef= useRef(null)
return <Son ref={SonRef}></Son>
}
import { forwardRef, useImperativeHandle } from 'react'
const Son = forwardRef((props, ref) => { // 1. 子组件接收到父组件传递的ref
useImperativeHandle(ref, () => { return {} }) // 2.1. 可以将子组件的数据和方法暴露出去
// 2.2 或者在子组件的HTML模版上绑定ref
return <div ref={ref}>
<button>子组件{count}</button>
</div>
})
export default Son
3. 用法二:存储变量
使用 ref 存储变量可以确保:
(1)可以在函数组件重新渲染时 存储信息(state数据变化会驱动试图更新,同时函数组件会重新执行,普通对象存储的值会被重置)。
(2)改变它 不会触发重新渲染(state数据变化会触发重新渲染)。
(3)对于组件的每个副本而言,这些信息都是本地的(外部变量则是共享的)。
四、自定义Hook
1. 什么是自定义Hook
自定义Hook就是一个以use开头的自定义函数。通过自定义Hook,可以实现 逻辑的封装和复用。
2. 使用
(1)定义一个 use开头的函数。
(2)在函数体内封装 可复用 的逻辑代码。
(3)将函数内用到的 state数据 和 方法 return出去。
(4)使用自定义Hook时,直接 调用函数,拿到state数据和方法。
五、 Hook基本使用规则
(1)只能在 React 的 函数组件 中调用 Hook。不能在普通 js函数或者类组件中调用。(自定义Hook中也可以调用Hook)
(2)只能在 函数组件最外层 调用 Hook。不要在 循环、条件判断 或者 子函数 中调用。
(3)如果想要有条件地执行一个 effect,可以将判断放到 Hook 的内部:
useEffect(function persistForm() {
// 将条件判断放置在 effect 中
if (name !== '') {
localStorage.setItem('formData', name);
}
});
(4)单个组件内,无论是State Hook或者Effect Hook,都可以 被多次调用。
六、useReducer Hook
1. 什么是useReducer
(1)userReducer是一个 React Hook,和useState相似都是用来 管理组件中的数据状态。
(2)不同的是,userReducer修改数据时,可以根据 不同的参数 去触发 不同的代码逻辑。
- 跟Redux有点像,都包含三个核心概念:state数据、action对象、reducers存放同步方法。
- 修改数据时调用dispatch函数,根据传入的action对象来触发reducers方法。
(只是这里的action对象是自定义对象,而Redux中是调用函数生成的action对象。)
2. 使用
(1.1)导入 useReducer 钩子函数并调用。接收两个参数,返回值是一个数组。
(1.2)第一个数组元素是 state 数据,第二个数组元素是 dispatch 函数。
(1.3)第一个入参是 自定义的reducer函数;第二个入参是 state初始值。
const [state, dispatch] = useReducer(myReducer, 初始值)
(2.1)自定义reducer函数,函数内部就是具体的业务逻辑。
(2.2)第一个参数是 state数据;第二个参数是 action对象,可以获取自定义参数,也可以判断要执行哪个业务逻辑。
function myReducer(state, action) {}
(3.1)调用 dispatch 函数,入参是一个 action 对象。
(3.2)action.type 用来区分要触发哪个业务逻辑;action.payload 是 自定义参数。
dispatch({type: '', payload: 自定义参数})
import { useReducer } from 'react'
// 自定义reducer函数
function myReducer(state, action) {
switch (action.type) {
case 'INC':
return state + action.payload
case 'DEC':
return state - action.payload
default:
return state
}
}
const Year = () => {
const [state, dispatch] = useReducer(myReducer, 0) // 调用useReducer钩子函数
return <div>
<button onClick={()=>dispatch({type: 'INC', payload: 10})}>自增运算</button>
<button onClick={()=>dispatch({type: 'DEC', payload: 20})}>自减运算</button>
</div>
}
export default Year
七、useMemo Hook
1. 什么是useMemo
(1)useMemo 是一个 React Hook,它在组件每次 重新渲染 的时候能够 缓存计算的结果,只有 指定的依赖项 变化时,才会重新 执行计算过程。
(2)数据驱动视图:React组件中任意state数据变化,都会引发组件 重新渲染,组件中调用的方法都会 重新执行。
(3)应用场景:消耗非常大的计算逻辑。
2. 使用
(1)导入 useMemo 钩子函数并调用。接收 两个参数。
(2)第一个参数是一个 回调函数,函数内部用来书写具体代码逻辑,函数必须有 返回值。
(3)第二个参数是 依赖项数组,可以是state数据、props数据、自定义变量和函数等。
const result = useMemo(()=>{ return num+1 }, [num])
import { useMemo, useState } from 'react'
// 计算量比较大的逻辑代码
function fn (value) { return value+ 1}
const Test = () => {
// 1.定义state数据,作为useMemo的依赖项
const [num ,setNum] = useState(0)
// 2.使用useMemo包裹计算量较大的代码逻辑,必须有返回值
const result = useMemo(()=>{
return fn(num)
}, [num])
return <div> </div>
}
export default Test
八、useCallback Hook
1. 什么是useCallback
(1)useCallback是一个React Hook, 主要用于性能优化,它的核心作用是 缓存函数引用,避免不必要的重渲染。
2. 使用
| 使用场景 | 说明 | 示例 |
|---|---|---|
| 子组件依赖函数props | 当函数作为props传递给被 memo 优化的子组件时 | 子组件用 React.memo 包裹,父组件的函数需要用 useCallback 缓存 |
| 依赖数组的依赖项 | 当函数作为其他 Hook 的依赖项时 | useEffect、useMemo 等的依赖数组中包含函数 |
| 稳定的函数引用 | 需要保持函数引用稳定的特殊场景 | 第三方库依赖、事件监听器等需要稳定函数引用 |
九、memo AIP
1. 什么是memo
(1)memo是一个函数,可以让子组件在 props 没有变化时,跳过重新渲染的过程。
(2)React渲染机制:父组件发生重新渲染时,子组件 也会 重新渲染。
2. 使用
(1)导入memo函数,并用memo函数 包裹子组件,返回一个新的 mome子组件。
const memoComponent = memo(function myComponent(props){ } )
3. 判断props是否变化的机制
(1)React使用 Object.is() 方法判断新旧props是否发生变化。对于引用数据类型它比较的是数据的引用地址。
(2)对于 引用数据类型,如 数组、对象、函数,组件每次渲染时都会产生新的数据,props 必然发生变化。
(3)如果想对 稳定引用,可以在父组件中用
钩子函数包裹数组或对象,暂时缓存。
const arr = [1,2,3]
const newArr = useMemo(()=>{return arr},[ ])
(4)如果想对 稳定引用,可以在父组件中用
钩子函数包裹函数,暂时缓存。
const handleClick = useCallback((value)=>{console.log(value)},[ ])
十、 第三方Hooks插件
1. ahooks 的 useGetState 是一个自定义 Hook,用于解决在异步操作中获取最新 state 值的问题。
(1)在 React 中,直接使用 useState 时,如果在异步回调(如 setTimeout、Promise、事件监听等)中访问 state,获取到的是闭包中的旧值,而不是最新的 state 值。
import { useGetState } from 'ahooks';
function SolutionComponent() {
const [count, setCount, getCount] = useGetState(0);
const handleAsync = () => {
setTimeout(() => {
console.log(getCount()); // 这里获取的是最新的 count 值
// 无论何时调用,都能获取到最新的 state
}, 1000);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={handleAsync}>异步打印最新值</button>
</div>
);
}
2. useAsyncEffect 是 ahooks 中的一个自定义 Hook,用于在 useEffect 中处理异步操作。
(1)在 React 的原生 useEffect 中,不能直接使用 async/await,因为 async 函数会返回 Promise,而 useEffect 应该返回清理函数或不返回任何内容。
import { useAsyncEffect } from 'ahooks';
function SolutionComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useAsyncEffect(async () => {
try {
setLoading(true);
const result = await fetch('/api/data');
const jsonData = await result.json();
setData(jsonData);
} catch (error) {
console.error('获取数据失败:', error);
} finally {
setLoading(false);
}
}, []);
if (loading) return <div>加载中...</div>;
return <div>数据: {JSON.stringify(data)}</div>;
}
十一、React内置组件
1. Fragment
无论是类组件、函数组件中,HtML结构最外层的<div></div>标签,可以替换为:
<Fragment> </Fragment> 或是 <> </>
① Fragment可以接收一个 key属性 用于遍历时的 唯一标识。
② 空标签不能接收任何属性。