Hooks are a new feature in Act 16.8. You can use state and other React features without writing a class. Allows you to reuse state logic without modifying component structure

Why use hooks

  1. Hooks make code more logical, can isolate common methods, common components
  2. Hooks can replace class declarations with function declarations, which are components
  3. Hooks can break a class component into small parts.

The main hooks

  • userCallback
  • useContext
  • useEffect
  • useLayoutEffect
  • useMemo
  • useReducer
  • useRef
  • useState

The use of hooks

A hook can only be called on the outermost layer of a function. Do not call in loops, conditional judgments, or subfunctions. Only use hooks in function components

useStateData store, distribute updates

The function is re-executed each time useState is rendered. When the function completes, all memory is freed. Creating a load of the current function component inside the function provides a way to modify state. UseState only performs initial assignment on the first rendering. The next execution does not get the initial value, but the cached value in the closure

import React, { useState } from 'react';
const DemoState = () = > {
  let [count, setCount] = useState(0);
  const handleAdd = () = > {
    count++;
    // Count can be arbitrary, and state changes are asynchronous
    setCount(count);
  };
  return (
    <div>
      <h1>I am a hook component</h1>
      <p>The state - > {count}</p>
      <button onClick={handleAdd}>Add 1</button>
    </div>
  );
};
export default DemoState;
Copy the code

UseState can be initialized with a callback, and setCount can be initialized with a callback

const num = 2;
let [count, setCount] = useState(() = > {
  return num * 10;
});
setCount(() = > ++count);
Copy the code
useEffectSide effect operation

Side effect → Logic that does not occur during data to view conversion is divided into function components that need to be cleaned up and function components that do not need to be cleaned up, pure functions, and props. Fixed inputs always get fixed outputs.

  1. UseEffect is a combination of componentDidMound, componentDidUpdate, and componentWillUnmount
  2. UseEffect (FN) takes a function that is not executed until the component is rendered to the screen. If there is a return value, a function that clears the side effects is returned, otherwise it does not
  3. It usually does not need to be executed synchronously and does not block browser rendering. UseLayoutEffect can be used if synchronous execution is required
import React, { useState, useEffect } from 'react';
const DemoHook = () = > {
  const num = 2;
  let [count, setCount] = useState(() = > {
    return num * 10;
  });
  const handleAdd = () = > {
    setCount(() = > ++count);
  };
  // The Dom will not be executed until the rendering is complete
  useEffect(() = > {
    setTimeout(() = > {
      setCount((count) = > ++count);
    }, 1000);
  });
  return (
    <div>
      <h1>I am a hook component</h1>
      <p>The state - > {count}</p>
      <button onClick={handleAdd}>Add 1</button>
    </div>
  );
};
export default DemoHook;
Copy the code

If you run the code above, you’ll see that the numbers are constantly updated automatically

  1. After dom rendering is complete, side effects are executed (useEffect callback is executed)
  2. During side effect execution, count is changed and the state is changed, which causes dom re-rendering and side effect execution
  3. To solve this problem, you can pass a second argument [] to useEffect. The array defines the state value that the component needs to re-render. UseEffect will only be executed if the state value in the array changes. If only an empty array is passed, useEffect will not be executed after the component is rerendered. It can be used to optimize if we need to do something like cancel event listening or clear timer during the component destruction phase. The first parameter of the useEffect function returns a function to clear these side effects. The equivalent of componentWillUnmount. When to clear the side effect function:
  4. Before the component is unmounted
  5. If more than one useEffect is to be executed, the next useEffect is executed before
useLayoutEffectRender useEffect before update
  1. UseEffect execution order: Component update mount complete → rendering complete →useEffect callback executed
  2. UseLayoutEffect execution order: component update mount complete → Execute useLayoutEffect callback → page render useLayoutEffect will prevent page render, causing page lag
useContextGet the context freely

We can use useContext to get the context value passed by the parent component. The current value is the value set by the Provider of the most recent parent component. The useContext parameter is usually introduced by createContext. UseContext can be used instead of context.Consumer to retrieve the value stored in the Provider

  1. Create the provider. The TSX
import React, { useState, createContext, Context } from 'react';
export interface ContextValue {
  count: number;
  setCount: Function;
  add: Function;
  reduce: Function;
}
export const context: Context<any> = createContext({});
export const ContextProvider = (props: any) = > {
  let [count, setCount] = useState(10);
  const countVal = {
    count,
    setCount,
    add: () = > setCount(count + 1),
    reduce: () = > setCount(count - 1)};const { children } = props;
  return <context.Provider value={countVal}>{children}</context.Provider>;
};
Copy the code
  1. Create a subContext. TSX
import React, { useContext } from 'react';
import { context, ContextProvider } from './contextDemo';

const Context1 = () = > {
  const { count = 0, add, reduce } = useContext(context);
  return (
    <div>
      <p>{count}</p>
      <button onClick={()= >The add ()} > 1</button>
      <button onClick={()= >Reduce ()} > 1</button>
    </div>
  );
};
const subContext = () = > (
  <ContextProvider>
    <Context1 />
  </ContextProvider>
);
export default subContext;
Copy the code
useReducerRedux in stateless components
  1. useReducerThe first parameter accepted is a function, which can be considered as a reducer. The reducer parameters are the normal state and action, and the changed state is returned.
  2. useReducerThe second argument is the initial value of state.
  3. useReducerThe third argument is a callback function that takes state and modifies the initial value
  4. useReducerReturns an array whose first entry is the updated state value and whose second argument is the dispatch function that dispatches the update
  5. The dispatch function triggers an update of the component

    One useState dispatch update function that causes components to re-render is dispatch in useReducer.

import React, { useReducer } from 'react';
interface State {
  count: number;
  num: number;
}
interface Action {
  type: string; value? :any;
}
const initalState: State = {
  count: 0.num: 1
};
const reducer = (state: State, action: Action) = > {
  switch (action.type) {
    case 'add':
      return { ...state, count: state.count + 1 };
    case 'reduce':
      return { ...state, count: state.count - 1 };
    default:
      returnstate; }};const ReducerCom = () = > {
  const [state, dispatch] = useReducer(reducer, initalState);
  return (
    <div>
      <p>{state.count}</p>
      <button onClick={()= >Dispatch ({type: 'add'})}> Add one</button>
    </div>
  );
};
export default ReducerCom;
Copy the code

High order component

import React, { useReducer, FunctionComponent, Context, createContext } from 'react';
export const ProviderContext: Context<any> = createContext({});
// eslint-disable-next-line react/display-name
const HOCComponent = (reducer: any, initState: any) = > (Com: FunctionComponent) = > {
  // eslint-disable-next-line react/display-name
  return () = > {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const [state, dispatch] = useReducer(reducer, initState);
    return (
      <ProviderContext.Provider value={{ state.dispatch}} >
        <Com />
      </ProviderContext.Provider>
    );
  };
};

export default HOCComponent;
Copy the code

Advanced component use

import React, { useContext } from 'react';
import concat, { ProviderContext } from './HOCComponent';
export const initState = {
  count: 0
};
export const reducer = (state: any, action: Record<string.any>) = > {
  switch (action.type) {
    case 'add':
      return { ...state, count: state.count + 1 };
    default:
      returnstate; }};const ReducerCom2 = () = > {
  const { state, dispatch } = useContext(ProviderContext);
  return (
    <div>
      <p>{state.count}</p>
      <button onClick={()= >Dispatch ({type: 'add'})}> add 1</button>
    </div>
  );
};

export default concat(reducer, initState)(ReducerCom2);
Copy the code
useRefGet elements, cache data

UseRef has a parameter that can be used as the initial value of cached data, and the return value can be marked by the DOM element ref, which can be used to retrieve the marked element node.

import React, { useRef, useState } from 'react';
const RefDemo = () = > {
  const [value, setValue] = useState(' ');
  const inputDom = useRef<HTMLInputElement | null> (null);
  const handlerSubmit = () = > {
    const $input = inputDom.current;
    console.log($input);
    console.log($input? .value); };const reset = () = > {
    setValue(' ');
  };
  const inputChange = () = > {
    const $input = inputDom.current;
    if($input) { setValue($input.value); }};console.log(11);
  return (
    <div>
      <input type='text' ref={inputDom} value={value} onChange={()= > inputChange()} />
      <button onClick={()= >Submitted handlerSubmit ()} ></button>
      <button onClick={()= >The reset ()} > reset</button>
    </div>
  );
};

export default RefDemo;
Copy the code
useMemo
  1. useMemoIndependent rendering space can be formed, and components and variables can be updated according to the agreed rules
  2. useMemoRender conditions depend on the second parameter deps (see useEffect)
  3. useMemoYou can avoid unnecessary updates to the execution of unnecessary contexts
import React, { useMemo, useState } from 'react';
const useMemoDemo = () = > {
  const [list, setList] = useState([0.1.2]);
  const [number, setNum] = useState(0);
  const [count, setCount] = useState(0);
  const changeList = () = > {
    const a = [...list];
    a.push(a.length);
    setList(a);
  };
  UseMemo is reexecuted when either list or number is changed. UseMemo is not executed when count is changed
  // Count wrapped by useMemo does not change. You must wait until useMemo is re-executed to get the latest count
  const lists = useMemo(() = > {
    console.log('Executed again 😔');
    return (
      <div>
        <ul>{list.map((item) => {console.log('list rendered 🍺'); return<li key={item}>{item}</li>;
          })}
        </ul>
        <p>Number - > {number}</p>
        <span>The count - > {count}</span>
      </div>
    );
  }, [list, number]);
  return (
    <div>
      <div>
        <span>The count - > {count}</span>
        <button onClick={()= >SetCount (count + 1)} > count + 1</button>
      </div>
      <div>
        <span>Number - > {number}</span>
        <button onClick={()= >SetNum (number + 1)} > count + 1</button>
      </div>
      {lists}
      <button onClick={()= >For changeList ()} > modification</button>
    </div>
  );
};
export default useMemoDemo;
Copy the code
useCallbackUseMemo version of the callback function
  1. useCallbackanduseMemoThey all take the same arguments, are executed after their dependencies have changed, and return cached values
  2. The difference is thatuseMemoReturns the result of the function’s execution,useCallbackThe function is returned.
import React, { useCallback, ComponentProps, useState, useEffect } from 'react';
const SubCom = (props: ComponentProps<any>) = > {
  console.log('Updated 🚗');
  useEffect(() = > {
    props.logInfo('Rendered'); } []);return <div>sub</div>;
};
const CallbackDemo = () = > {
  const [count, setCount] = useState(0);
  const logInfo = useCallback((subName: string) = > {
    console.log(subName); } []);return (
    <div>
      <span>The count - > {count}</span>
      <button onClick={()= >SetCount (count + 1)}> add one</button>
      <SubCom logInfo={logInfo} />
    </div>
  );
};
export default CallbackDemo;
Copy the code