React常用Hooks

1、useReducer

作用:让 React 管理多个相对关联的状态数据

javascript 复制代码
import { useReducer } from 'react'
​
// 1. 定义reducer函数,根据不同的action返回不同的新状态
function reducer(state, action) {
  switch (action.type) {
    case 'INC':
      return state + 1
    case 'DEC':
      return state - 1
    default:
      return state
  }
}
​
function App() {
  // 2. 使用useReducer分派action
  const [state, dispatch] = useReducer(reducer, 0)
  return (
    <>
      {/* 3. 调用dispatch函数传入action对象 触发reducer函数,分派action操作,使用新状态更新视图 */}
      <button onClick={() => dispatch({ type: 'DEC' })}>-</button>
      {state}
      <button onClick={() => dispatch({ type: 'INC' })}>+</button>
    </>
  )
}
​
export default App

更新流程

  1. 使用 dispatch 函数触发 reducer 函数
  2. reducer 函数内部根据不同的 action type 返回不同的状态
  3. 使用最新返回的状态渲染UI

分派action传参

做法:分派action时如果想要传递参数,需要在action对象中添加一个payload参数,放置状态参数

javascript 复制代码
// 定义reducer
​
import { useReducer } from 'react'
​
// 1. 根据不同的action返回不同的新状态
function reducer(state, action) {
  console.log('reducer执行了')
  switch (action.type) {
    case 'INC':
      return state + 1
    case 'DEC':
      return state - 1
    case 'UPDATE':
      return state + action.payload
    default:
      return state
  }
}
​
function App() {
  // 2. 使用useReducer分派action
  const [state, dispatch] = useReducer(reducer, 0)
  return (
    <>
      {/* 3. 调用dispatch函数传入action对象 触发reducer函数,分派action操作,使用新状态更新视图 */}
      <button onClick={() => dispatch({ type: 'DEC' })}>-</button>
      {state}
      <button onClick={() => dispatch({ type: 'INC' })}>+</button>
      <button onClick={() => dispatch({ type: 'UPDATE', payload: 100 })}>
        update to 100
      </button>
    </>
  )
}
​
export default App

2、useMemo

作用: 它在每次重新渲染的时候能够缓存计算的结果

下面我们的本来的用意是想基于count的变化计算斐波那契数列之和,但是当我们修改num状态的时候,斐波那契求和函数也会被执行,显然是一种浪费

javascript 复制代码
// useMemo
// 作用:在组件渲染时缓存计算的结果
​
import { useState } from 'react'
​
function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}
​
function App() {
  const [count, setCount] = useState(0)
  // 计算斐波那契之和
  const sumByCount = factorialOf(count)
​
  const [num, setNum] = useState(0)
​
  return (
    <>
      {sum}
      <button onClick={() => setCount(count + 1)}>+count:{count}</button>
      <button onClick={() => setNum(num + 1)}>+num:{num}</button>
    </>
  )
}
​
export default App

useMemo缓存计算结果

思路: 只有count发生变化时才重新进行计算

scss 复制代码
import { useMemo, useState } from 'react'
​
function fib (n) {
  console.log('计算函数执行了')
  if (n < 3) return 1
  return fib(n - 2) + fib(n - 1)
}
​
function App() {
  const [count, setCount] = useState(0)
  // 计算斐波那契之和
  // const sum = fib(count)
  // 通过useMemo缓存计算结果,只有count发生变化时才重新计算
  const sum = useMemo(() => {
    return fib(count)
  }, [count])
​
  const [num, setNum] = useState(0)
​
  return (
    <>
      {sum}
      <button onClick={() => setCount(count + 1)}>+count:{count}</button>
      <button onClick={() => setNum(num + 1)}>+num:{num}</button>
    </>
  )
}
​
export default App

React.memo

作用:允许组件在props没有改变的情况下跳过重新渲染

组件默认的渲染机制

默认机制:顶层组件发生重新渲染,这个组件树的子级组件都会被重新渲染

javascript 复制代码
// memo
// 作用:允许组件在props没有改变的情况下跳过重新渲染
​
import { useState } from 'react'
​
function Son() {
  console.log('子组件被重新渲染了')
  return <div>this is son</div>
}
​
function App() {
  const [, forceUpdate] = useState()
  console.log('父组件重新渲染了')
  return (
    <>
      <Son />
      <button onClick={() => forceUpdate(Math.random())}>update</button>
    </>
  )
}
​
export default App

使用React.memo优化

机制:只有props发生变化时才重新渲染 下面的子组件通过 memo 进行包裹之后,返回一个新的组件MemoSon, 只有传给MemoSon的props参数发生变化时才会重新渲染

javascript 复制代码
import React, { useState } from 'react'
​
const MemoSon = React.memo(function Son() {
  console.log('子组件被重新渲染了')
  return <div>this is span</div>
})
​
function App() {
  const [, forceUpdate] = useState()
  console.log('父组件重新渲染了')
  return (
    <>
      <MemoSon />
      <button onClick={() => forceUpdate(Math.random())}>update</button>
    </>
  )
}
​
export default App

props变化重新渲染

javascript 复制代码
import React, { useState } from 'react'
​
const MemoSon = React.memo(function Son() {
  console.log('子组件被重新渲染了')
  return <div>this is span</div>
})
​
function App() {
  console.log('父组件重新渲染了')
​
  const [count, setCount] = useState(0)
  return (
    <>
      <MemoSon count={count} />
      <button onClick={() => setCount(count + 1)}>+{count}</button>
    </>
  )
}
​
export default App

props的比较机制

对于props的比较,进行的是'浅比较',底层使用 Object.is 进行比较,针对于对象数据类型,只会对比俩次的引用是否相等,如果不相等就会重新渲染,React并不关心对象中的具体属性

javascript 复制代码
import React, { useState } from 'react'
​
const MemoSon = React.memo(function Son() {
  console.log('子组件被重新渲染了')
  return <div>this is span</div>
})
​
function App() {
  // const [count, setCount] = useState(0)
  const [list, setList] = useState([1, 2, 3])
  return (
    <>
      <MemoSon list={list} />
      <button onClick={() => setList([1, 2, 3])}>
        {JSON.stringify(list)}
      </button>
    </>
  )
}
​
export default App

说明:虽然俩次的list状态都是 [1,2,3] , 但是因为组件App俩次渲染生成了不同的对象引用list,所以传给MemoSon组件的props视为不同,子组件就会发生重新渲染

自定义比较函数

如果上一小节的例子,我们不想通过引用来比较,而是完全比较数组的成员是否完全一致,则可以通过自定义比较函数来实现

javascript 复制代码
import React, { useState } from 'react'
​
// 自定义比较函数
function arePropsEqual(oldProps, newProps) {
  console.log(oldProps, newProps)
  return (
    oldProps.list.length === newProps.list.length &&
    oldProps.list.every((oldItem, index) => {
      const newItem = newProps.list[index]
      console.log(newItem, oldItem)
      return oldItem === newItem
    })
  )
}
​
const MemoSon = React.memo(function Son() {
  console.log('子组件被重新渲染了')
  return <div>this is span</div>
}, arePropsEqual)
​
function App() {
  console.log('父组件重新渲染了')
  const [list, setList] = useState([1, 2, 3])
  return (
    <>
      <MemoSon list={list} />
      <button onClick={() => setList([1, 2, 3])}>
        内容一样{JSON.stringify(list)}
      </button>
      <button onClick={() => setList([4, 5, 6])}>
        内容不一样{JSON.stringify(list)}
      </button>
    </>
  )
}
​
export default App

3、useCallback

上一小节我们说到,当给子组件传递一个引用类型prop的时候,即使我们使用了memo 函数依旧无法阻止子组件的渲染,其实传递prop的时候,往往传递一个回调函数更为常见,比如实现子传父,此时如果想要避免子组件渲染,可以使用 useCallback缓存回调函数

javascript 复制代码
// useCallBack
​
import { memo, useState } from 'react'
​
const MemoSon = memo(function Son() {
  console.log('Son组件渲染了')
  return <div>this is son</div>
})
​
function App() {
  const [, forceUpate] = useState()
  console.log('父组件重新渲染了')
  const onGetSonMessage = (message) => {
    console.log(message)
  }
​
  return (
    <div>
      <MemoSon onGetSonMessage={onGetSonMessage} />
      <button onClick={() => forceUpate(Math.random())}>update</button>
    </div>
  )
}
​
export default App

useCallback 缓存函数

useCallback 缓存之后的函数可以在组件渲染时保持引用稳定,也就是返回同一个引用

javascript 复制代码
// useCallBack
​
import { memo, useCallback, useState } from 'react'
​
const MemoSon = memo(function Son() {
  console.log('Son组件渲染了')
  return <div>this is son</div>
})
​
function App() {
  const [, forceUpate] = useState()
  console.log('父组件重新渲染了')
  const onGetSonMessage = useCallback((message) => {
    console.log(message)
  }, [])
​
  return (
    <div>
      <MemoSon onGetSonMessage={onGetSonMessage} />
      <button onClick={() => forceUpate(Math.random())}>update</button>
    </div>
  )
}
​
export default App

4、forwardRef

作用:允许组件使用ref将一个DOM节点暴露给父组件

javascript 复制代码
import { forwardRef, useRef } from 'react'
​
const MyInput = forwardRef(function Input(props, ref) {
  return <input {...props} type="text" ref={ref} />
}, [])
​
function App() {
  const ref = useRef(null)
​
  const focusHandle = () => {
    console.log(ref.current.focus())
  }
​
  return (
    <div>
      <MyInput ref={ref} />
      <button onClick={focusHandle}>focus</button>
    </div>
  )
}
​
export default App

5、useImperativeHandle

作用:如果我们并不想暴露子组件中的DOM而是想暴露子组件内部的方法

javascript 复制代码
import { forwardRef, useImperativeHandle, useRef } from 'react'
​
const MyInput = forwardRef(function Input(props, ref) {
  // 实现内部的聚焦逻辑
  const inputRef = useRef(null)
  const focus = () => inputRef.current.focus()
​
  // 暴露子组件内部的聚焦方法
  useImperativeHandle(ref, () => {
    return {
      focus,
    }
  })
​
  return <input {...props} ref={inputRef} type="text" />
})
​
function App() {
  const ref = useRef(null)
​
  const focusHandle = () => ref.current.focus()
​
  return (
    <div>
      <MyInput ref={ref} />
      <button onClick={focusHandle}>focus</button>
    </div>
  )
}
​
export default App
相关推荐
PleaSure乐事3 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
getaxiosluo3 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
新星_4 小时前
函数组件 hook--useContext
react.js
阿伟来咯~5 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端5 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱5 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
bysking6 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
September_ning11 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人11 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱00111 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js