React Hooks
React Hooks 使用规则
使用规则
只能在组件或者其他自定义 Hook 函数中调用
只能在组件的顶层调用, 不能嵌套在 if、for、其他函数中
useState
useState
是一个 React hook(函数), 它允许我们想组建添加一个状态变量,从而控制影响组件的渲染结果
本质:和普通 JS 变量不同的是,状态变量一旦发生变化组件的视图 UI 也会跟着变化(数据驱动视图)
基本语法:
const [state, setState] = useState(0)
- useState 是一个函数,返回值是一个数组
- 数组中的第一个参数是状态变量,第二个参数是 set 函数,用来修改状态变量
- useState 的参数将作为 count 的初始值
状态不可变: 在 React 中,状态被认为是只读的,我们应该始终替换它而不是修改它,直接修改状态不能引发视图更新
const [state, setState] = useState(0)
// 错误,不能直接修改状态变量
const handleClick = () => {
count++
console.log(count)
}
// 正确,通过 setState 修改状态变量
const handleClick = () => {
setCount(count + 1) // 使用新值直接替换原值
}
修改对象状态
规则:对于对象类型的状态变量,应该始终传给 set 方法一个全新的对象来进行修改
const [form, setForm] = useState({
name: '张三'
})
// 错误,直接修改原对象不会引发视图变化
const handleErrorChangeName = () => {
form.name = '李四'
}
// 正确,调用set传入新对象用于修改
const handleChangeName = () => {
// 使用新值直接替换原值
setForm({
...form,
name: 'join'
})
}
useRef
在 React 组件中获取/操作 DOM,需要使用 useRef 钩子函数。
const inputRef = useRef(null)
<input type="text" ref={inputRef} />
useEffect
useEffect 是一个 React hook 函数, 用于在 React 组件中创建不是由事件引起而是由渲染引起的操作,比如发送 AJAX 请求,更改 DOM 等等
说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于 只由渲染引起的操作
基础使用
需求:在组件渲染完毕之后,立刻从服务器端获取频道列表数据显示到页面中
useEffect(() => {}, [])
/**
* 参数1:一个函数,可以把它叫做副作用函数,在函数内部可以放置到要执行的操作
* 参数2:一个数组(可选参),在数组中防止依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组时,副作用函数只会在组件渲染完毕之后执行一次
*/
useEffect 依赖项参数说明
useEffect 副作用函数的执行时机存在多种情况,根据 传入依赖项的不同
, 会有不同的执行表现
依赖项 | 副作用函数执行时机 |
---|---|
没有依赖项 | 组件初始渲染 + 组件更新时机 |
空数组依赖 | 只在初始渲染执行一次 |
添加特定依赖项 | 组件初始渲染 + 特性依赖项变化时执行 |
useEffect 清除副作用
在 useEffect 中编写的 由渲染本身引起的对接组件外部的操作
,社区也经常把它叫做副作用操作,比如在 useEffect 中开发一个定时器,我们想在组件卸载时把这个定时器在清理掉,这个过程就是清理副作用。
useEffect(() => {
// 实现副作用操作逻辑
return () => {
// 清除副作用逻辑
}
}, [])
说明:清除副作用的函数最常见的执行时机是在 组件卸载时自动执行
useReducer
作用: 和 useState
类似,用来管理 相对复杂 的状态数据
语法场景:
- 定义一个 reducer 函数(根据不同的 action 返回不同的新状态)
- 在组件中调用 useReducer,传入 reducer 函数和初始状态
- 在组件中 dispatch action,触发 reducer 函数的执行,并更新组件的状态
import { useReducer } from 'react'
function reducer(state, action) {
switch (action.type) {
case 'INC':
return state + 1
case 'DEC':
return state - 1
default:
return state
}
}
function UseReducerExample() {
const [count, dispatch] = useReducer(reducer, 0)
return (
<div>
this is useReducer
<button onClick={() => dispatch({ type: 'INC' })}>+</button>
{count}
<button onClick={() => dispatch({ type: 'DEC' })}>-</button>
</div>
)
}
export default UseReducerExample
useMemo
作用:在组件每次重新渲染的时候 缓存计算结果
基础语法:
说明:使用 useMemo
做缓存之后可以保证只有 count1 依赖项发生变化时才会重新计算。
备注:默认只要状态发生变化,所依赖的函数都会重新执行
useMemo(() => {
// 根据依赖项计算结果
}, [依赖项])
React.memo
作用: 在组件每次重新渲染的时候缓存计算的结果
useCallback
作用: 在组件多次重新渲染的时候缓存函数
基础语法:
const MemoInput = memo(function Input({onChange}) => {
return <input type="text" onChange={() => onChange(e.target.value)} />
})
function App() {
// 传给子组件的函数
const changeHandler = useCallback(value => console.log(value), [])
// 触发父组件重新渲染的函数
const [count, setCount] = useState(0)
return (
<div>
<MemoInput onChange={handleInputChange} />
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
)
}
自定义 hook 函数
概念: 自定义 Hook 是以 use开头的函数
, 通过自定义 Hook 函数可以用来实现逻辑的封装和复用
业务场景:点击 toggle 按钮 ,控制盒子 div 的显示/隐藏
// 不封装直接实现
function App() {
const [value, setValue] = useState(false)
const toggle = () => setValue(!value)
return (
<div>
{value && <div>this is div</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
// 封装自定义Hook实现
function useToggle() {
// 可复用的逻辑代码
const [value, setValue] = useState(false)
const toggle = () => setValue(!value)
// 哪些状态和糊掉函数需要再其他组件 中使用return
return { value, toggle }
}
function App() {
const { value, toggle } = useToggle() // 调用自定义 hook 函数
return (
<div>
{value && <div>this is div</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
封装自定义 hook 通用思路
- 声明一个以 use 开头的函数
- 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
- 把组件中用到的状态或者回调 return 出去(以对象或者数组的形式)
- 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用