Skip to content

React Hooks

React Hooks 使用规则

使用规则

  • 只能在组件或者其他自定义 Hook 函数中调用

  • 只能在组件的顶层调用, 不能嵌套在 if、for、其他函数中

useState

useState 是一个 React hook(函数), 它允许我们想组建添加一个状态变量,从而控制影响组件的渲染结果

本质:和普通 JS 变量不同的是,状态变量一旦发生变化组件的视图 UI 也会跟着变化(数据驱动视图)

基本语法:

jsx
const [state, setState] = useState(0)
  1. useState 是一个函数,返回值是一个数组
  2. 数组中的第一个参数是状态变量,第二个参数是 set 函数,用来修改状态变量
  3. useState 的参数将作为 count 的初始值

状态不可变: 在 React 中,状态被认为是只读的,我们应该始终替换它而不是修改它,直接修改状态不能引发视图更新

jsx
const [state, setState] = useState(0)

// 错误,不能直接修改状态变量
const handleClick = () => {
  count++
  console.log(count)
}

// 正确,通过 setState 修改状态变量
const handleClick = () => {
  setCount(count + 1) // 使用新值直接替换原值
}

修改对象状态

规则:对于对象类型的状态变量,应该始终传给 set 方法一个全新的对象来进行修改

jsx
const [form, setForm] = useState({
  name: '张三'
})

// 错误,直接修改原对象不会引发视图变化
const handleErrorChangeName = () => {
  form.name = '李四'
}

// 正确,调用set传入新对象用于修改
const handleChangeName = () => {
  // 使用新值直接替换原值
  setForm({
    ...form,
    name: 'join'
  })
}

useRef

在 React 组件中获取/操作 DOM,需要使用 useRef 钩子函数。

jsx
const inputRef = useRef(null)

<input type="text" ref={inputRef} />

useEffect

useEffect 是一个 React hook 函数, 用于在 React 组件中创建不是由事件引起而是由渲染引起的操作,比如发送 AJAX 请求,更改 DOM 等等

useEffect

说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于 只由渲染引起的操作

基础使用

需求:在组件渲染完毕之后,立刻从服务器端获取频道列表数据显示到页面中

jsx
useEffect(() => {}, [])

/**
 * 参数1:一个函数,可以把它叫做副作用函数,在函数内部可以放置到要执行的操作
 * 参数2:一个数组(可选参),在数组中防止依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组时,副作用函数只会在组件渲染完毕之后执行一次
 */

useEffect 依赖项参数说明

useEffect 副作用函数的执行时机存在多种情况,根据 传入依赖项的不同, 会有不同的执行表现

依赖项副作用函数执行时机
没有依赖项组件初始渲染 + 组件更新时机
空数组依赖只在初始渲染执行一次
添加特定依赖项组件初始渲染 + 特性依赖项变化时执行

useEffect 清除副作用

在 useEffect 中编写的 由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在 useEffect 中开发一个定时器,我们想在组件卸载时把这个定时器在清理掉,这个过程就是清理副作用。

jsx
useEffect(() => {
  // 实现副作用操作逻辑

  return () => {
    // 清除副作用逻辑
  }
}, [])

说明:清除副作用的函数最常见的执行时机是在 组件卸载时自动执行

useReducer

作用: 和 useState 类似,用来管理 相对复杂 的状态数据

语法场景:

    1. 定义一个 reducer 函数(根据不同的 action 返回不同的新状态)
    1. 在组件中调用 useReducer,传入 reducer 函数和初始状态
    1. 在组件中 dispatch action,触发 reducer 函数的执行,并更新组件的状态
jsx
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 依赖项发生变化时才会重新计算。

备注:默认只要状态发生变化,所依赖的函数都会重新执行

jsx
useMemo(() => {
  // 根据依赖项计算结果
}, [依赖项])

React.memo

作用: 在组件每次重新渲染的时候缓存计算的结果

useCallback

作用: 在组件多次重新渲染的时候缓存函数

基础语法:

jsx
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 的显示/隐藏

useEffect

jsx
// 不封装直接实现
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>
  )
}
jsx
// 封装自定义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 通用思路

    1. 声明一个以 use 开头的函数
    1. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
    1. 把组件中用到的状态或者回调 return 出去(以对象或者数组的形式)
    1. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用