React Hook -E

90 阅读4分钟

Why use hook in rect?

  • In React, hooks are a way to add stateful behavior and other features to functional components.

  • Before the React version 16.8, stateful behavior could only be achieved in class components using the state object and lifecycle methods like componentDidMount, componentDidUpdate, etc.

useState

  • setState支持stateless组件有自己的state

  • 入参:具体值或一个函数

  • 返回值:数组

    • 第一项是state值

    • 第二项负责派发数据更新,组件渲染

setState会让组件重新执行 => 配合useMemo或useCallback

  • 当更新函数之后,state的值是不能即时改变的,只有当下一次上下文执行的时候,state值才随之改变

useEffect

two arguments

  1. A function that represents the side effect you want to perform.

  2. An optional array of dependencies (dependencies array).

use

  1. Performing Side Effects

    • You can use useEffect to execute code that has side effects

    • like fetching data from an API, updating the DOM, setting up event listeners, etc.

    • Since these side effects should not block the rendering of the component, useEffect allows them to happen after the component has rendered.

  2. Running Effects Once (ComponentDidMount)

    1. If you pass an empty array ([]) as the second argument to useEffect, the effect function will only run once, after the initial render.

    2. This is similar to the componentDidMount lifecycle method in class components.

  3. Running Effects When Dependencies Change (ComponentDidUpdate):

    1. The effect will run whenever any of the dependencies in the array change.

    2. This is useful for scenarios where you want to re-run the effect based on specific state or prop changes

    • similar to the componentDidUpdate lifecycle method in class components.
  4. Cleaning Up Effects (ComponentWillUnmount):

    1. If the effect function returns a cleanup function, React will run this cleanup function when the component is unmounted or before the effect is run again due to a dependency change.

    2. This helps you clean up any resources or subscriptions created by the effect, preventing memory leaks.

useEffect无法直接使用async await

useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('https://xxx.com')
    const json = await response.json()

    setData(json)
  }

  // call the function
  fetchData()
    // make sure to catch any error
    .catch(console.error)
}, [])

监听多个属性变化

import React, { useEffect } from 'react'

function MyComponent({ prop1, prop2 }) {
  useEffect(() => {
    // 当 prop1 或 prop2 发生变化时执行的代码
    console.log('prop1 或 prop2 发生了变化')

    // 在这里可以执行你想要的操作,比如发送网络请求、更新状态等等
    if (prop1 && prop2) {
      // 当 prop1 和 prop2 都满足条件时执行的代码
      console.log('prop1 和 prop2 都发生了变化')

      // 在这里可以执行你想要的操作,比如发送网络请求、更新状态等等
    }
  }, [prop1, prop2])

  return (
    // 组件的 JSX
    111
  )
}

export default MyComponent

javascript - React hook 使用useEffect 如何判断多个值都改变了才执行内部方法? - SegmentFault 思否

useLayoutEffect

  • useEffect:flashing

    • Component update mount completed -> The browser dom drawing is completed -> Execute the useEffect callback
  • useLayoutEffect:Stuck and stopped

    • Component update mount completed -> Execute the useLayoutEffect callback-> The browser dom drawing is completed

useContext

  • 用来获取父级组件传递过来的context值

    • 这个当前值就是最近的父级组件 Provider 的value
  • 从parent comp获取ctx方式

    • useContext(Context)

    • Context.Consumer

/* 用useContext方式 */
const DemoContext = () => {
  const value = useContext(Context)
  /* my name is aaa */
  return <div> my name is {value.name}</div>
}

/* 用Context.Consumer 方式 */
const DemoContext1 = ()=>{
  return <Context.Consumer>
    {/*  my name is aaa  */}
    { (value)=> <div> my name is { value.name }</div> }
  </Context.Consumer>
}

export default () => {
  return (
    <div>
      <Context.Provider value={{ name: 'aaa' }}>
        <DemoContext />
        <DemoContext1 />
      </Context.Provider>
    </div>
  )
}

useReducer

  • 入参

    • 第一个为函数,可以视为reducer

      • 包括state 和 action
    • 返回值 state (base on action)

    • 第二个为state的初始值

  • 出参

    • 第一个是更新后的state值

    • 第二个是派发更新的dispatch函数

      • 执行dispatch会导致组件re-render

        • (另一个是useState)
  • useReducer+useContext 代替Redux

const DemoUseReducer = () => {
  /* number为更新后的state值,  dispatchNumber 为当前的派发函数 */
  const [number, dispatchNumber] = useReducer((state, action) => {
    const { payload, name } = action
    /* return的值为新的state */
    switch (name) {
      case 'a':
        return state + 1
      case 'b':
        return state - 1
      case 'c':
        return payload
    }
    return state
  }, 0)
  return (
    <div>
      当前值:{number}
      {/* 派发更新 */}
      <button onClick={() => dispatchNumber({ name: 'a' })}>增加</button>
      <button onClick={() => dispatchNumber({ name: 'b' })}>减少</button>
      <button onClick={() => dispatchNumber({ name: 'c', payload: 666 })}>赋值</button>
      {/* 把dispatch 和 state 传递给子组件  */}
      <MyChildren dispatch={dispatchNumber} State={{ number }} />
    </div>
  )
}

useMemo & useCallback

  • useMemo is used for caching computation results, while useCallback is used for caching function instances. Both hooks aim to optimize React components by controlling when and how often certain calculations and functions are recalculated or recreated."

Custom Hooks - implement a function

update hook

import { useState } from 'react'

const useUpdate = () => {
  const [, setFlag] = useState()
  const update = () => {
    setFlag(Date.now())
  }
  return update
}

export default useUpdate

// 实际使用
const App = (props) => {
  // ...
  const update = useUpdate()
  return (
    <div>
      {Date.now()}
      <div>
        <button onClick={update}>update</button>
      </div>
    </div>
  )
}

useScroll hooks

import { useState, useEffect } from 'react'

const useScroll = (scrollRef) => {
  const [pos, setPos] = useState([0, 0])

  useEffect(() => {
    function handleScroll(e) {
      setPos([scrollRef.current.scrollLeft, scrollRef.current.scrollTop])
    }
    scrollRef.current.addEventListener('scroll', handleScroll)
    return () => {
      scrollRef.current.removeEventListener('scroll', handleScroll)
    }
  }, [])

  return pos
}

export default useScroll

// 用法
import React, { useRef } from 'react'
import { useScroll } from 'hooks'

const Home = (props) => {
  const scrollRef = useRef(null)
  const [x, y] = useScroll(scrollRef)

  return (
    <div>
      <div ref={scrollRef}>
        <div className="innerBox"></div>
      </div>
      <div>
        {x}, {y}
      </div>
    </div>
  )
}

Hooks VS HOC

  1. Hook

    • put more related logic together => replace the lifecycle

    • Suitable for Controller or related logic that needs cohesion

  2. HOC

    • Put external property functionality into a base Component => Plug-ins for extensibility

      • Extend the functionality of components by injecting stateful props instead of writing code directly in the main library

Why can't react hooks be placed in if and for?

  1. Rules of Hooks: React enforces strict rules for using hooks. Hooks, such as useState, useEffect, etc., must always be called at the top level of a function component or another custom hook. They should not be placed inside conditionals or loops.

  2. Order of Execution: Hooks rely on the order in which they are called during each render. Placing them inside conditionals or loops can lead to variations in the order of execution, causing unexpected results and bugs.

  3. Avoiding State Inconsistency: Placing hooks inside conditionals or loops can lead to inconsistencies in state updates. React won't be able to guarantee that the same hook will always be executed for the same component, potentially resulting in incorrect behavior.

  4. Alternative Approaches: To work with conditional logic or loops in React components, use hooks outside the conditionals or loops. You can control rendering behavior based on the state or props without violating the rules of hooks.

  • Hooks can only be used in the top-level scope of function components