This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Today I’m going to share with you something important in React: Hooks. I’m going to use the following directory to analyze the basic use of Hooks in my project.

  • Existing problems

  • The purpose of the Hooks

  • Which are fixed by Hooks

  • Common Hooks

    • useState
    • useRender
    • useContext
    • useEffct
    • useCallback
    • useMemo
    • useRef
  • Customize the Hook

Problems with class components

Counter class

 class Example extends Component{
     constructor(props){
        super(a);this.state={
            count:0
        } 
     }
    addNum = () = >{
        this.setState({
            count:this.state.count + 1})}render(){
         return(
             <div>
                    <p>this num is {this.state.count}</p>
                    <button onClick={this.addNum}>click me</button>
             </div>)}}Copy the code

Existing problems:

  • Use the bind or arrow function to constrain the scope of this in our function

  • State logic becomes difficult to reuse and complex components become difficult to understand

React also supports function components

function App(props) {
  return <h1>Hello, {props}</h1>;
}
Copy the code

But there is a limitation. Pure functions have no state, no life cycle, and therefore cannot replace classes

The purpose of the Hooks

React Hooks are designed to enhance function components so that they can write a fully functional component without using “classes” at all

A hook is a way to hook in state and other React features

Use whatever hooks you need. React provides some common hooks by default, but you can also encapsulate your own.

All hooks introduce external functions to functions. Therefore, the React convention uses the use prefix to name hooks for easy identification.

Counter hooks

function Example (){
    const [count,setCount] = useState(0);
    return(
        <>
            <p>the num is {count}</p>
            <button onClick={()= >{setCount(count+1)}}>click me </button>
        </>)}Copy the code

Which are fixed by Hooks

  • A Hook can only be called from the outermost layer inside a function, not from a loop, a judgment, or a child function

  • You can only call a Hook in the React function component, not in a JavaScript function

Q: Why can only be called from the outermost layer inside a function?

The reason:

Multiple hooks are written in a single component, and the order in which they are called is determined by where they are written. Its internal implementation is actually a linked list of calls. There is no guarantee that it will work properly if you can’t make sure that every render is in the same position.

Common Hook

Examples in projects

importReact, {useState, useEffect, useCallback}from 'react';

function APP() {
   const [refreshCount, setRefreshCount] = useState<number>(0)
   const [list, setList] = useState<ClusterDO[] | null> ([])const handleUpdate = useCallback(
    () = > setRefreshCount((prevState) = > prevState + 1),
    [])
    
   useEffect(() = > {
    const fetchData = async() = > {const result = await clusterInfo(uniqueId)

      if (result.success) {
        setList(result.data)
      } else {
        message.error(result.message)
        setList(null)
      }
    }

    fetchData()
  }, [uniqueId, refreshCount])

  return (
    <>
     <Table dataSource={list} />
      <button onClick={handleUpdate}>
        Refresh
      </button>
   </>
  );
}
Copy the code

uesState

// 1. Introduce React and useState
import React, { useState } from 'react';

function APP() {
  // 2. Declare state variable
   const [refreshCount, setRefreshCount] = useState<number>(0)
   const [list, setList] = useState<ClusterDO[] | null> ([])//3. Update variables
    const handleUpdate = () = > setRefreshCount((prevState) = > prevState + 1)

  return (
   <>//4. Use the defined variables<p>{refreshCount}</p>
      <button onClick={handleUpdate}>
        Refresh
      </button>
    </>
  );
Copy the code
  1. Why calluseState?
  2. useState()What is the return value of? What’s the use?
  3. Multiple callsuseState.ReactIf you know that everyuseStateWhat value does that correspond to?

Why call useState?

  • calluseStateI defined onestateStates can be used as variables. This isThe way a function holds variablesAll other variables disappear after the function exits, butstateWill be preserved

What is the return value of useState()? What’s the use?

  • Calling useState returns an array of items: a state and a function that updates the state

  • During initial rendering, the state returned is the same as the value of the first parameter passed in

  • We can call this update function in an event handler or some other place, and we can change the value of state by passing a new value to the update function, and it will just replace the old state with the new state

React if you know what value each useState corresponds to?

  • Chain calls

tips

Use multiple state variables (separate separate states)

  • UseState also supports passing objects and arrays, so it is possible to write multiple variables into an object. However, since state updates replace its values rather than merge them, it is recommended that multiple variables be defined separately and changed simultaneously. Another advantage of writing this way is that the two variables can be extracted more easily in different components

      // It is not recommended to write all states in one object like this
      const [state, setState] = React.useState({
        isFetching: false.startTime: null.endTime: null,})// Update one of the methods
      setState((prevState) = > ({ ...prevState, isFetching: false }))
    Copy the code

Problems encountered

! [image-20210712235354343](/Users/admin/Library/Application Support/typora-user-images/image-20210712235354343.png)

  1. Why is the output of the setState function unchanged immediately after execution

  2. The value of state.selectedrowKeys for the next click will still be empty. Why is it not the value that was set last time?

  3. Why does the render state.selectedrowkeys output have the latest value?

    Why is the output of the setState function unchanged immediately after execution

    Answer: The update status method returned by useState is “asynchronous” and will not get the new value until the next redraw. Do not try to get the state immediately after changing it

    The value of state.selectedrowKeys for the next click will still be empty. Why is it not the value that was set last time?

    A: The value state.selectedRowKeys is not added to the useCallback dependency, so it will always be the first value, but the first render value will be empty

    Why does the render state.selectedrowkeys output have the latest value?

    A: The log is synchronous and gets the latest value during re-rendering

Classic interview questions

  • In the reactsetStateIs it synchronous or asynchronous? When is it asynchronous, can it be synchronous, and when is it synchronous?

    • SetState is “asynchronous” only in synthesized events and hook functions, and synchronous in both native events and setTimeout.

    • The “asynchronous” of setState does not mean that it is internally implemented by asynchronous code, in fact, the process and code itself are synchronous, but the call order of the synthesized event and hook function is before the update, so that the updated value cannot be immediately obtained in the synthesized event and hook function, which forms the so-called “asynchronous”. Of course, the callback in the second parameter setState(partialState, callback) can get the updated result.

    • The batch update optimization of setState is also based on “asynchronous” (composite event, hook function). Batch update will not be performed in the native event and setTimeout. In “asynchronous”, if setState is performed for the same value for many times, the batch update strategy of setState will overwrite it. Take the last execution, if setState is multiple different values at the same time, it will be merged during the update batch update.

    tips

    React Is an event object that simulates the functionality of native DOM events. It is a cross-browser wrapper for browser native events. It is defined by W3C standards, is compatible with all browsers, and has the same interface as native events

    React all events are synthesized and are not native DOM events. However, native DOM events can be obtained by using e.ativeEvent

    // Native DOM events
    <button onclick="activateLasers()">Activate Lasers</button>
    
    //React synthesizes events
    <button onClick={activateLasers}>Activate Lasers</button>
    Copy the code

    Characteristics of composite events

    • Name in camel case, not all lowercase
    • Using JSX syntax requires passing in a function as an event handler, not a string

useReducer

It is an alternative to useState. It receives a Reducer of the form (state, action) => newState and returns the current state and its accompanying dispatch method.

const [state, dispatch] = useReducer(reducer, initialArg, init)
Copy the code

Usage scenarios

UseReducer can be more useful than useState in some situations, such as when the state logic is complex and contains multiple subvalues, or when the next state depends on the previous state.

Also, using useReducer can optimize performance for components that trigger deep updates because you can pass dispatches to child components instead of callbacks

An example of rewriting the useState counter

const initialState = {count: 0};

function 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

tips

Specify initial state: The initial value is passed in as the second argument

 const [state, dispatch] = useReducer(
    reducer,
    {count: initialCount}
  )
Copy the code

Lazy initialization: The function created is passed in as the third argument to the useReducer. The initial state is the argument to the function

Doing so extracts the logic used to calculate the state outside the Reducer, which also facilitates future actions to reset the state

function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button// Reset buttononClick={()= > dispatch({type: 'reset', payload: initialCount})}>
        Reset
      </button>
      <button onClick={()= > dispatch({type: 'decrement'})}>-</button>
      <button onClick={()= > dispatch({type: 'increment'})}>+</button>
    </>
  );
}
Copy the code

Skip Dispatch (Idempotence of useReducer)

If the Reducer Hook returns the same value as the current state, React skips the rendering of the child components and the execution of the side effects. (React uses the Object.is comparison algorithm to compare states.)

useEffect

  • UseEffect can perform side effects in function components, most commonly fetching background data

  • UseEffect Hook can be regarded as the combination of three life cycle functions componentDidMount, componentDidUpdate and componentWillUnmount

     useEffect(() = > {
        const fetchData = async() = > {const result = await clusterInfo(uniqueId)
    
          if (result.success) {
            setList(result.data)
          } else {
            message.error(result.message)
            setList(null)
          }
        }
    
        fetchData()
      }, [uniqueId, refreshCount])  // Only re-render when the page is first rendered and the uniqueId or refreshCount changes
    Copy the code

    UseEffect () is executed whenever the array changes. The first argument is a function that can hold asynchronous operations, and the second argument is an array that holds dependencies

  • UseEffect can be classified into an effect that needs to be cleared and an effect that does not need to be cleared

    Actions that don’t need to be cleaned: After React updates the DOM, some extra code is run, such as sending network requests, manually updating the DOM, and printing logs, which don’t need to be cleaned

    Operations to clear: subscribes to external data sources. Clearing prevents memory leaks

    How to implement cleanup?

    • ifeffectReturns a function,ReactIt will be called when the cleanup is performed
      useEffect(() = > {
        function handleStatusChange(status) {
          setIsOnline(status.isOnline);
        }
    
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        // Implement cleanup
        return () = > {
          ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
      },[props.friend.id]
    Copy the code
    1. calluseEffectWhat did you do?
    2. useEffectDoes it render every time?
    3. When the purge will be performedeffect?

What does the call to useEffect do?

  • Through thishookCan you tellReact The actions that the component will perform after rendering,ReactWill save the function passed inDOMCall after update.

Will useEffect render every time?

  • useEffectIt is executed on the first render and every update, and what controls it is that it is triggered when the variables in the second argument list are updated

When a clean effect is performed

  • During component uninstallationReactA cleanup operation is performed

tips

Use effects with separate logic

Using multiple effects to separate different logic into different effects allows for separation of concerns, and React calls each Effect in the component in the order in which it is declared.

useCallback

const memoizedCallback = useCallback(
  () = > {
    doSomething(a, b);
  },
  [a, b],
);
Copy the code

Returns a Memoized callback function.

Passing the inline callback function and an array of dependencies as arguments to useCallback returns the version of the callback function that is updated only when a dependency changes.

Use reference equality to avoid unnecessary rendering when you pass callback data to the optimized

The dependency array is not passed as an argument to the callback function. All values referenced in the callback function should appear in the dependency array.

UseCallback (fn, deps) equivalent to useMemo(() => fn, deps)

The difference between useMemo and useCallback is that useMemo caches the return value of a function, while useCallback caches the function itself. Both apis are performance optimization methods

Memoization is an optimization technique primarily used to speed up program computations by allowing functions to avoid repeating calculations of previously processed inputs and return cached results

useMemo

const memoizedValue = useMemo(() = > computeExpensiveValue(a, b), [a, b]);
Copy the code

Returns an memoized value.

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.

Functions passed into useMemo are executed during rendering.

You can put theuseMemoAs a means of performance optimization, but not as a semantic guarantee.

useRef

UseRef returns a mutable ref object whose.current property is initialized as the passed parameter (initialValue).

const refContainer = useRef(initialValue)
Copy the code

The ref object returned remains the same throughout the life of the component, meaning that the same ref object is returned each time the function component is re-rendered (using react.createref, refs are recreated each time the component is re-rendered)

Usage Scenarios:

3. UseRef is used as a variable, and modifying it does not render

function TextInputWithFocusButton() {
  const echEL = React.useRef(null)
 
React.useEffect(() = > {
    constmyChart = echarts.init(echEL.current) .... And []})return (
    <div ref={echEL} className="echartsSty"></div>
  );
}
Copy the code

useContext

Prior knowledge: Context

Context is a way to pass data across the component tree without manually adding Props for each layer of components

Role: Share global data for a component tree.

Main application scenario: Many components at different levels need to access the same data. Use caution because this makes components less reusable.

UseContext takes a context object and returns the current value of that context. The current value of the current context. The current context value is determined by the < MyContext.provider > value prop of the upper-layer component closest to the current component.

When the most recent < myContext. Provider> update is made to the component’s upper layer, the Hook triggers a rerender and uses the latest context value passed to the Provider.

const themes = {
  light: {
    foreground: "# 000000".background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff".background: "# 222222"}};const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background.color: theme.foreground}} >
      I am styled by theme context!
    </button>
  );
}
Copy the code

Custom Hooks

State logic between shared components can use custom hooks (or render props and higher-order components). Custom hooks are a function that starts with use and calls other hooks from within a custom Hook

export function useClusterDetail(uniqueId: string) :ClusterDetailResult {
  const [cluster, setCluster] = React.useState<ClusterDO | null> (null)
  const [refreshCount, setRefreshCount] = React.useState<number>(0)

  React.useEffect(() = > {
    const fetchData = async() = > {const result = await clusterInfo(uniqueId)

      if (result.success) {
        setCluster(result.data)
      } else {
        $notification.error(result.message)
        setCluster(null)
      }
    }

    fetchData()
  }, [uniqueId, refreshCount])

  return { cluster, setRefreshCount }
}

/ / use
  const { cluster, setRefreshCount } = useClusterDetail(uniqueId)
Copy the code

The above are some of my React Hook usages in the project, as well as the pitfalls I usually encounter. Later, I will also explore and share some advanced usages. If it is helpful to you, please give me a thumbs up or follow me and grow with me.