React Hooks 用法详解

什么是Hook

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

常用的hook

useState

语法

const [xxx, setXxx] = useState(initValue) 

参数:

  • initValue:第一次初始化指定的值在内部作缓存

返回值:

  • 包含2个元素的数组,第1个为内部当前状态值,第2个为更新状态值的函数

setXxx()2种写法:

  • setXxx(newValue) : 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
  • setXxx(value => newValue) : 参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

例子:

import React, { useState } from 'react'



const UseStateDemo = () => {
  const [count, setCount] = useState(0)
  const onAdd = () => {
    setCount(count + 1)
  }
  return (
    <>
      <p> {count}</p>
      <button onClick={() => onAdd()}>Count++</button>
    </>
  )
}
export default UseStateDemo

useEffect

语法

useEffect(() => { 
  // 在此可以执行任何带副作用操作
  return () => { // 在组件卸载前执行
    // 在此做一些收尾工作, 比如清除定时器/取消订阅等
  }
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

参数说明:

  • useEffect()接受两个参数,第一个参数是要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项,只要这个数组发生变化,useEffect()里面的异步操作就会执行。
  • 当第二项省略不填时,useEffect()会在每次组件渲染时都会执行useEffect。
  • 当第二项传 空数组[ ] 时,只会在组件挂载后运行一次。

返回值:

  • useEffect()返回值可以是一个函数,在组件销毁的时候会被调用。清理这些副作用可以进行如取消订阅、清除定时器操作。

useRef

useRef可以在函数组件中存储、查找组件内的标签或任意其它数据

useRef返回一个可变的ref对象,useRef接受一个参数绑定在返回的ref对象的current属性上,返回的ref对象在整个生命周期中保持不变。

作用:保存标签对象,功能与React.createRef()一样

例子:

import React, { useRef } from 'react'
const UseRefDemo = () => {
  const inputRef = useRef<HTMLInputElement>(null)

  const handleOK = () => {
    console.log(inputRef.current?.value)
  }
  return (
    <div>
      <input ref={inputRef} />
      <button onClick={handleOK}>OK</button>
    </div>
  )
}
export default UseRefDemo

useMemo

useMemo() 主要用来解决使用React hooks产生的无用渲染的性能问题

语法:

onst cachedValue = useMemo(calculateValue, dependencies)

参数:

  • cachedValue:第一个参数为一个函数,函数的返回值作为缓存值
  • dependencies:放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 依赖项里面的状态,是否有改变,如果有改变重新执行 cachedValue ,得到新的缓存值。

useMemo应用场景:

  • 可以缓存 element 对象,从而达到按条件渲染组件,优化性能的作用。
  • 如果组件中不期望每次 render 都重新计算一些值,可以利用 useMemo 把它缓存起来。
  • 可以把函数和属性缓存起来,作为 PureComponent 的绑定方法,或者配合其他Hooks一起使用

例子:

import React, { useState, useMemo, useRef } from 'react'
const UseMemoDemo = () => {
  const inputRef = useRef<HTMLInputElement>(null)

  const [text, setText] = useState('')
  const [data, setData] = useState([
    { id: 1, name: 'test1111' },
    { id: 2, name: 'test222' },
    { id: 3, name: 'test334' },
    { id: 4, name: 'test444' }
  ])

  const getList = useMemo(() => {
    return data.filter(item => {
      if (item.name.includes(text)) {
        return item
      }
    })
  }, [text])
  const onSearch = () => {
    setText(inputRef.current!.value)
  }
  return (

    <div>
      <input ref={inputRef} />
      <button onClick={onSearch}>查询</button>
      {getList.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  )
}
export default UseMemoDemo

useCallback

缓存的是函数

语法:

const cachedFn = useCallback(fn, dependencies)

例子:

import React, { useState, useCallback } from 'react'
interface ChildProps {
  result: () => number
}
const Child = React.memo((props: ChildProps) => {
  console.log('child-render()')
  return <div>I'm Child Component: {props.result()}</div>
})
function UseCallbackDemo() {
  const [count, setCount] = useState(0)
  const [val, setVal] = useState(0)
  const getResult = useCallback(() => {
    // 下面计算假设是比较复杂,耗时的计算,否则没必要缓存
    return count ** 2
  }, [count])
  return (
    <div>
      <p>count: {count}</p>
      <p>val: {val}</p>
      <Child result={getResult} />
      <button onClick={() => setCount(count + 1)}>add count</button>
      <button onClick={() => setVal(val + 2)}>add val</button>
    </div>
  )
}
export default UseCallbackDemo

useContext

共享状态钩子作用就是可以做状态的分发。常用于【祖组件】与【后代组件】间通信。

例子:

import React, { useContext } from 'react'



const GlobalContent = React.createContext({ color: 'red', background: 'green' })
function UseContextDemo() {
  return (
    <GlobalContent.Provider value={{ color: 'blue', background: 'red' }}>
      <ChildComp />
    </GlobalContent.Provider>
  )
}
function ChildComp() {
  return (
    <div>
      <GrandchildComp />
    </div>
  )

}



function GrandchildComp() {
  const context = useContext(GlobalContent)
  const { color, background } = context
  return (

    <p>
      color: {color} ========== background: {background}
    </p>
  )
}

export default UseContextDemo

useReducer

useReduceruseState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
定义:
const [state, dispatch] = useReducer(reducer, initialArg, init);
例子:

//	reducer 计数器
const initialState = {count: 0}


const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1}
    case 'decrement':
      return {count: state.count - 1}
    default:
      throw new Error()
  }


};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

useLayoutEffect

useLayoutEffect() :和useEffect相同,都是用来执行副作用,但是它会在所有的DOM变更之后同步调用effect。useLayoutEffect和useEffect最大的区别就是一个是同步,一个是异步。

从这个Hook的名字上也可以看出,它主要用来读取DOM布局并触发同步渲染,在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

官网建议还是尽可能的是使用标准的useEffec以避免阻塞视觉更新。

useImperativeHandle

可以在使用 ref 时自定义暴露给父组件的实例值。

例子:

import { useEffect, useRef, useImperativeHandle } from 'react'
import { forwardRef } from 'react'


const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null)
  useImperativeHandle(ref, () => ({
    focus: () => {
      if (inputRef.current) {
        inputRef.current.value = 'Hello'
      }
    }
  }))
  return <input ref={inputRef} />
})

const ImperativeHandleDemo = () => {
  const ref = useRef<HTMLInputElement>(null)
  useEffect(() => {
    ref.current?.focus()
  })
  return (
    <>
      <FancyInput ref={ref} />
    </>
  )
}
export default ImperativeHandleDemo

React18 新增的hook

useId

生成唯一 ID

语法:

const id = useId()

例子

import { useId } from 'react'



const UseIdDemo = () => {
  const id1 = useId()
  const id2 = useId()
  const id3 = useId()
  const id4 = useId()
  const id5 = useId()
  return (

    <div>
      <h1>useId</h1>
      <p>id1 === {id1}</p>
      <p>id2 === {id2}</p>
      <p>id3 === {id3}</p>
      <p>id4 === {id4}</p>
      <p>id5 === {id5}</p>
    </div>
  )
}
export default UseIdDemo

useTransition

useTransition 是一个让你在不阻塞 UI 的情况下来更新状态的 React Hook。

语法:

const [isPending, startTransition] = useTransition()

例子:

import React, { useState, useTransition } from 'react'
const UseTransitionDemo = () => {
  const getProducts = () => {

    const dataList: any[] = []

    for (let i = 0; i < 30000; i++) {

      dataList.push({

        id: i,

        name: `产品${i}`

      })

    }

    return dataList

  }


  const [isPending, startTransition] = useTransition()
  const [searchText, setSearchText] = useState('')
  const [products, setProducts] = useState<any>([])
  const handleChangeText = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchText(e.target.value)
    startTransition(() => {
      const list = getProducts().filter(product => product.name.includes(e.target.value))
      setProducts(list)
    })
  }
  const productComp = () => {
    return products.map(product => {
      return (
        <div
          style={{
            height: '40px',
            lineHeight: '40px',
            background: 'skyblue',
            marginTop: '8px',
            textAlign: 'center',
            color: 'white'
          }}
        >
          {product.name}
        </div>
      )
    })
  }
  return (
    <div style={{ width: '200px', margin: '0 auto' }}>
      <input value={searchText} onChange={handleChangeText} />
      {isPending && <p>正在输入</p>}
      {productComp()}
    </div>
  )
}
export default UseTransitionDemo

useDeferredValue

需要接收一个值, 返回这个值的副本, 副本的更新会在值更新渲染之后进行更新, 以此来避免一些不必要的重复渲染。

语法:

const deferredValue = useDeferredValue(value)

例子:

import React, { useState, useDeferredValue } from 'react'
const UseDeferredValueDemo = () => {
  const getProducts = () => {

    const dataList: any[] = []

    for (let i = 0; i < 30000; i++) {

      dataList.push({

        id: i,

        name: `产品${i}`

      })

    }

    return dataList

  }


  const [searchText, setSearchText] = useState('')
  const [products, setProducts] = useState<any>([])
  const handleChangeText = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchText(e.target.value)
    const list = getProducts().filter(product => product.name.includes(e.target.value))
    setProducts(list)
  }
  const defferedProducts = useDeferredValue(products)
  const productComp = () => {
    return defferedProducts.map(product => {
      return (
        <div
          style={{
            height: '40px',
            lineHeight: '40px',
            background: 'skyblue',
            marginTop: '8px',
            textAlign: 'center',
            color: 'white'
          }}
        >
          {product.name}
        </div>
      )
    })
  }
  return (
    <div style={{ width: '200px', margin: '0 auto' }}>
      <input value={searchText} onChange={handleChangeText} />
      {productComp()}
    </div>
  )
}
export default UseDeferredValueDemo

useTransition和useDeferredValue区别

  1. 对同一个资源的优化,这两个接口的提供的优化效果是一样的,因此不需要同时使用,
    也就是说使用一个就行了,因为一旦使用这两个任何一个都会带来一定性能上的损耗。
  2. 建议只有数据量大的时候考虑使用这两个接口中的一个,平时的普通组件不需要使用,
    原因是使用这两个任何一个都会带来一定性能上的损耗。
  3. 既然使用哪个接口都一样,为啥做了两个接口?因为:useTransition是用来处理更新
    函数的,而useDeferredValue:是用来处理更新函数执行后所更新的数据本身的。有些情
    况下,你并不能直接获得更新函数,比如你是用的是第三方的ooks库,你在使用的时候
    更新函数并不能直接对外暴露,这时候你就只能去优化数据,从而只能使用
    useDeferredValue,useTransition的好处是它可以一次性的处理好几个更新函数。

useSyncExternalStore

useSyncExternalStore是一个可以订阅组件外部数据的Hook。
语法:

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

例子:

import React, { useSyncExternalStore } from 'react'
function subscribe(callback) {
  window.addEventListener('online', callback)
  window.addEventListener('offline', callback)
  return () => {
    window.removeEventListener('online', callback)
    window.removeEventListener('offline', callback)
  }
}

function useOnlineStatus() {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine,
    () => true
  )

}



const UseSyncExternalStore = () => {
  const isOnline = useOnlineStatus()
  return <div>Hello, {isOnline ? '在线' : '离线'}</div>
}
export default UseSyncExternalStore

useInsertionEffect

useInsertionEffect是React中的一个自定义hook,它允许开发人员在元素被插入到DOM中时执行一些操作。在React中,元素被插入到DOM中通常是在组件挂载时发生的。useInsertionEffect的语法类似于React的其他hook,它接受一个回调函数作为参数,该回调函数会在元素被插入到DOM中时被调用。

语法:

useInsertionEffect(setup, dependencies?)

例子:

import { useInsertionEffect } from 'react';
import './styles.css';
 
function MyComponent() {
  useInsertionEffect(() => {
    document.querySelector('.my-element').classList.add('fade-in');
  });
  
  return (

    <div className="my-element">Hello World!</div>
  );
}

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYXakCEB' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片