react hook

This is mainly about hook syntax and usage scenarios

hook

A Hook is a special function that uses JavaScript closures to allow you to “Hook” function components with React state and lifecycle features. Hooks cannot be used in class components. This is why I started with the functional component cable

The order in which hooks are called is the same in every render, so it works just fine. As long as the order in which hooks are called is consistent across multiple renders, React correctly associates the internal states with the corresponding hooks. But what happens if we put a Hook call into a conditional statement?

Answer: The order of Hook calls has changed and there are bug Hook rules

userState

React is a hook that allows you to update UI state asynchronously and in response to data changes in the React function component.

The userState function initializes the value of a variable and returns an array containing the first item of the initialized variable and the second item of the name of the method that modifies it in response.

import React, { useState } from 'react';

function Example() {// declare a state variable called "count". We can declare many const [count,setCount] = useState<number>(0); // Array destruct, in typescript, we can declare state types const [fruit,setFruit] = useState<string>('banana');

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setClick me </button> </div>; }Copy the code

The second argument returned by userState can accept a function, which can be passed to setState if the new state needs to be computed using the previous state. This function takes the previous state and returns an updated value. Note that useState does not automatically merge update objects, so the operator is used to merge update objects.

function Box() {
  const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
    useEffect(() => {
    functionHandleWindowMouseMove (e) {// Expand "...state "to make sure we don't" lose "width and heightsetState(state => ({ ... state, left: e.pageX, top: e.pageY })); }}); // No second argument, only render once, never repeat}Copy the code

Normally, we use the userState hook and pass it a simple value, but if the initial state needs to be computed by a complex calculation, we can pass in a function that calculates and returns the initial state, which is called only during the initial render

const [state, setState] = useState(() => {
  return  doSomething(props);
});
Copy the code

The update status method returned by useState is asynchronous, and the status is the latest value when the next event cycle is executed. Do not try to get the state immediately after changing it. This is not a REATC bug, it’s because JavaScript behaves normally, it’s because of closures

Functional components and class components online difference Demo

For example, when we use the set structure in immutable. Js, we loop over items that are always the last item in the array

infos.forEach((el) => {
  if(the list from the (el. Id)) {setList(list.delete(item.id))// This is asynchronous, the page has not been redrawn when you loop, can not get the last value}})Copy the code

If we want to delete inside the loop, how do we do it? Remember, useState wanted us to change the DOM rendering directly, so we used it. We can modify the whole thing first and then affect DOM rendering

  infos.forEach((el) => {
    if(list.has(el.id)) {list = list.delete(el.id)}}setList(List)// Modify the structure of the DOMCopy the code

React is designed for performance purposes. It is easy to understand how to change all states and only redraw them once. Internally, merge the new state and the old state and return a new state object. When a closure such as setTimeout appears in a component, try to reference ref instead of state inside the closure, otherwise the old value may be read. The closure refers to the old value, but once it passes through setUsetate, it refers to a new object with a different address than the original object.

Variables that directly affect the DOM, so we call them states. When a variable has no impact on the DOM, it is not wise to define it as an asynchronous variable. A good way is to define it as a synchronization variable.

React Hooks Keep track of asynchronous operations

useReducer

We can use useReducer to replace useState, which is similar to vuex in Vue, when we encounter multiple useState influencing each other and need or only one parameter is different. It’s a little bit Redux, but just a little bit, a whole different concept

const initialState = {count: 0}; // Multiple usestates are combined into onefunction 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>
    </>
  );
}

Copy the code

useReducer

The State Reducer Pattern with React Hooks

React Hooks architecture – layering

Umi Hooks – Help embrace React Hooks

Effect Hook

React waits for the browser to complete rendering before delaying useEffect, which is equivalent to the React class’s three lifecycle functions componentDidMount. A combination of three life cycle functions, componentDidUpdate (component update) and componentWillUnmount (component to be destroyed), reduces the need to write repetitive code

ComponentDidMount: When a component is mounted, a bunch of things need to be executed

ComponentDidUpdate (watch) executes an Effect Hook function when the data you are listening to changes.

ComponentWillUnmount: A cleanup effect for situations where you need to clean up some data to avoid a memory leak. Return a function that represents the cleanup operation you want to do. Not returning a function means no cleanup is required. (Component uninstallation,

  const [debounceVal, setDebounceVal] = useState(value)
  useEffect(() => {
    const handle = setTimeout(() => {
      setDebounceVal(value)
    }, delay)
    return() => {clearTimeout(handle) // clear timer when a component is depleted} // eslint-disable-next-line react-hooks/ Exhaustive deps}, [value])return debounceVal
Copy the code

By default, it is executed after the first render and after every update, and the effect cleanup phase is executed at every re-render. This can cause performance problems, so it is also called a side effect. He can accept the second parameter. He will compare the two data before and after the update. If there is no change, he will not execute the hook. It is executed only if the second parameter changes. This avoids unnecessary repeated rendering and cleanup operations

You can pass an empty array ([]) as the second argument. This tells React that your effect doesn’t depend on any value in props or state, so it never needs to be repeated. [] is a reference type value. In some cases, custom hooks are used as the second argument, which also cause the page to be rerendered. Detail in custom hook

UseEffect Complete Guide -> This is very well written and is especially recommended

React Hooks # 4: useEffect

useMemo

If we have CPU intensive operations, we can optimize the use by storing the results of the initial operations in the cache. If the operation is bound to be executed again, we won’t bother using our CPU again, because the result of the same result is stored somewhere, we will simply return it to memory to speed it up, react. useMemo is the new hooks API, And while the API works on the Function component, this method exists only as a way to optimize performance. But don’t rely on it to “block” rendering, as this can be buggy.

You pass in the create function and the dependency array as arguments to useMemo, which recalculates memoized values only when a dependency changes. This optimization helps avoid costly calculations every time you render.

function App() {
  const [num, setNum] = useState(0); // result returns a value of 49995000function expensiveFn() {
    let result = 0;

    for (let i = 0; i < 10000; i++) {
      result += i;
    }

    console.log(result) // 49995000
    returnresult; } const base = expensiveFn(); // const base = useMemo(expensiveFn, []); It is executed only on the first click, and then it is not executed. Its second argument means the same as useEffectreturn (
    <div className="App"<button onClick={() => <h1>count: {num}</h1>setNum(num + base)}>+1</button>
    </div>
  );
}
Copy the code

Remember that functions passed into useMemo are executed during rendering. Please do not perform non-rendering operations inside this function. Operations such as side effects are used by useEffect, not useMemo

useCallback

When a parent component passes a function to a child component, each modification of the parent component rerenders, causing them to have a different reference on each render. As a result, each modification of the parent component directly causes the child component to render unnecessarily. (Reference type

At this point we’ll pass the function and dependencies as arguments to useCallback, which will return a Memoizedversion of the callback function that will be updated only if the dependency changes.

Render the same results given the same props, and improve the performance of the component by memorizing the render results of the component, the second parameter represents the same thing

// Avoid duplicate rendering of reference types

  const handleIndicator = useCallback((indicator: EvaluateIndicator) => {
    console.log('Pass to word component')}, [])Copy the code

// Function buffeting

import React, { useState, useCallback } from 'react'
import { debounce } from '.. /.. /utils/tool'

import './index.scss'
interface searchlParams {
  handleSearch: (val: string) => void
}
const Search: React.FC<searchlParams> = ({ handleSearch }) => {
  const [value, setValue] = useState<string>(' '*/ const debounceSearch = useCallback(debounce((val) => handleSearch(val), 2000), [], ) const changhandleSearch = (e: any) => {setValue(e.target.value)
    debounceSearch(e.target.value)
  }
  return (
    <div className="search-wrapper">
      <input className="input-control" value={value} onChange={changhandleSearch} placeholder="Search" />
    </div>
  )
}
Copy the code

The children need to work with the use of React. Memo and useCallback to reduce the number of rerenders

Both useCallback and useMemo can cache a reference or value to a function, but in a more subtle sense useCallback caches a reference to a function, useMemo caches the value of calculated data

How to optimize React functional components

React Performance Optimization Direction

UseCallback, useMemo analysis & differences

React.memo

Can reduce the number of times to rerender.

/ / child componentfunction Child(props) {
  console.log(props.name)
  return <h1>{props.name}</h1>
}

exportDefault react. memo(Child) // Parent componentfunction App() {
  const [count, setCount] = useState<number>(1)

  return (
    <div className="App">
      <h1>{ count }</h1>
      <button onClick={() => setCount(Count +1)}> </button> <Child name="sunseekers"></Child>
    </div>
  );
}
Copy the code

If your function component is rendering the same results given the same props, you can improve the performance of the component by wrapping it in a react.Memo call to remember the results of the component’s rendering. This means that in this case React will skip the render component and simply reuse the results of the last render. (If not wrapped in react. memo, the subcomponents will be rerendered every time the count changes)

Check only the PROPS changes. If the function component is wrapped in react. Memo and its implementation has a Hook for useState or useContext, it will still re-render when the context changes. By default, only shallow comparisons are performed on complex objects. If you want to control the comparison process, pass in your custom comparison function as a second argument

How to optimize React functional components

useRef

Equivalent to refs in VUE, just used differently here. UseRef returns a mutable REF object whose current property is initialized as the passed parameter (initialValue). The returned REF object remains the same throughout the life of the component and can be used to solve problems with stale value references due to closure issues

function TextInputWithFocusButton() { const inputEl = useRef(null); Const onButtonClick = () => {// 'current' points to the text input element inputel.current.focus () mounted to the DOM; };return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
Copy the code

It is executed twice during the update process, first passing in the null parameter and then passing in the DOM element parameter a second time, so two data messages can be printed out in the control module

Refs and the DOM

Refs references Demo through functions

The State Reducer Pattern with React Hooks

Customize the Hook

When we use the same code in multiple component functions, and the code contains the React hook, we can extract it into a third function. Components and hooks are functions, so the same applies.

A custom Hook is a function whose name starts with “use”. Other hooks can be called inside the function. Using the same Hook in two components does not share state, it is a separate state

  1. Interface request, each interface is preceded by a loading
import { useState, useCallback, useEffect } from 'react'

export function useFriendStatus(fn, dependencies) {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(falseLoading const Request = useCallback(() => {setLoading(true)
    setData(fn)
    setLoading(falseUseEffect (() => {request() // eslint-disable-next-line react-hooks/ down-deps}, [dependencies])return{// request data, // loading state loading, // request method encapsulation request,}} // use const {data, loading } = useFriendStatus(fetchTodos({ tab:'activeTab' }), 'activeTab')

Copy the code

If your dependencies are of a reference type, you need to add the following dependencies to your page: For example, if we give useFriendStatus the second parameter an empty array, the interface page will be rerendered every time it is requested, the second parameter’s empty array reference address changes, causing an infinite loop, try it yourself

  1. Function image stabilization
//@ts-ignore
import React, { useState, useEffect } from 'react'
export default function useDebounce(value, delay) {
  const [debounceVal, setDebounceVal] = useState(value)
  useEffect(() => {
    const handle = setTimeout(() => {
      setDebounceVal(value)
    }, delay)
    return () => {
      clearTimeout(handle)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value])
  return// Use interface searchlParams {handleSearch: (val: string) => void } const Search = ({ handleSearch:searchlParams }) => { const [value,setValue] = useState<string>(' ') // The function is buffered, and every internal variable change is registered and executedsetA Timeout, Const debounceSearch = useDebounce(value, 2000) useEffect(() => { handleSearch(value) // eslint-disable-next-line react-hooks/exhaustive-deps }, [debounceSearch])return (
    <div className="search-wrapper">
      <input className="input-control" value={value} onChange={(e) => setValue(e.target.value)} placeholder="Search" />
      {/* <i className="iconfont ico search-ico"> &#xe6aa;  */}
    </div>
  )
}
Copy the code

Use the anti-shock and throttling functions in the React function component

Customize the Hook

Use React Hooks + custom Hook encapsulation to build a complete small application step by step

Original link, will keep updated, it is suggested to pay attention to the original, the latest update