React Hooks have been out for a long time, some people have embraced Hooks, some people are so good at class that they need Hooks, others are left handed, right handed. That is in keeping with the hooks of the past century. Which is why I want to learn from myself that you are a man of the past century.

Why hooks?

More abstractable, logic reusable, code streamlined, avoid writing various life cycles. Is not the benefit of yao) and everyone’s use of the feeling to do a simple summary:

  • Reuse of common logic. Before hooks were born, React didn’t add reusability behavior to components to capabilities, and developers started using renderProps or higher-order components to do this. However, to be honest, these methods are cumbersome and require reorganizing the organization of the project or component. If we use React – DevTools to observe our React application, we will find that the code structure is full of abstract components with non-logical functions such as providers, consumers, high-order components, renderProps and so on, which forms a “nested hell”. Now that we have hooks, we can easily decouple common logic and reuse it better. This is in line with the componentalization idea of React.

  • Simplify code. React is component-based, but many components become behemoths as they are written. A lot of init and update methods are done in the Life cycle for componentDidMount or componentDidUpdate, and a lot of methods and timers need to be cleaned up in componentWillUnmount. This causes a lot of unrelated logic to get mixed up, and the cleanup effort may miss something. It is not possible to split components into smaller particles indefinitely, so don’t worry about that now. Hooks allow us to handle each state separately, clean it separately, and manage it more granular.

  • Stay away from this question. React uses dot bind to handle this pointing problems, but we use the arrow function in ES6 to reduce the amount of bind. However, this also illustrates the confusion caused by this in class.

So much for hooks, are hooks good for anything? Of course not, we still have to rely on class components in some cases.

The Hook equivalents for getSnapshotBeforeUpdate, getDerivedStateFromError and componentDidCatch life cycles are not yet supported, but we will work on them soon

experience

Use Estate in Hooks will replace data directly, not merge it like setState in class. This is a bit of trouble but we can define a method to do this, it’s not that hard. React also says that you should not make objects as complex as useState to value. We recommend smaller particles like data. What if it’s inconvenient to split, but state starts to get complicated again? You are advised to use useReducer(an alternative to useState) or custom hook.

Hooks to some usage considerations:

Why do I see stale props and state in my functions? For example:

import React, {useState} from 'react';
function Example() {
  const [count, setCount] = useState(0);
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

export default Example;
Copy the code

If I click the Show Alert button first, then click Click Me immediately to increase count to 5. How many browser alerts will pop up? 0! It’s not 5. Why is that? This is because any function inside a component, including event handlers and effects, is “seen” from the render in which it was created. That is, the value of the state is recorded when the method is executed. So what do we do if we encounter something like asynchrony? Use ref to save, modify, and read. We modify the code as follows:

function Example() { let countRef = useRef(0) const handleAlertClick=()=> { setTimeout(() => { alert('You clicked on: ' + countRef.current); }, 3000); } return ( <div> <button onClick={() => countRef.current++}> Click me </button> <button onClick={handleAlertClick}> Show  alert </button> </div> ); }Copy the code

There’s another case where you see old props or state. When we use useEffect, the second parameter passes in an inappropriate dependency. For example, if I change username but use [] or [age] where I depend, I can either remove the dependency array directly or pass in the appropriate dependency. One of the things about this is that thinking about ref before is like I defined an object variable at the beginning, why don’t I just define obj? This is because every time you re-render your obj will be re-executed, so you get the initial value every time.

How do I get the state of the previous round?

Use the ref! The following code looks like this:

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  return <h1>Now: {count}, before: {prevCount}</h1>;
}

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}
Copy the code

Can a function component reference a function component?

It is not recommended, but you can expose some imperative methods to the parent using the useImperativeHandle Hook. In the following code, the render parent can call the focus method inside FancyInput via inputref.current. Focus ().

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
Copy the code

How do I measure DOM nodes?

When it comes to DOM, the ref comes to mind first, but generally we prefer to use useCallback to get the DOM size and position. React calls callback every time a ref is attached to a node, as shown in the following example:

function test(){ const [height, setHeight] = useState(0); const measureRef = useCallback(node =>{ if (node ! == null) { setHeight(node.getBoundingClientRect().height); Callback ={measuredRef} {measuredRef ={measuredRef} {measuredRef ={measuredRef}} world</h1> <h2>The above header is {Math.round(height)}px tall</h2> </> ) }Copy the code

So why don’t we use useRef? UseRef does not notify us of ref changes when ref is an object, but using callback ensures that the parent component will receive information to update the data even if the child component is displayed late or after some interaction or size change.

The actual use of Hooks

useState

The useState update is not a merge update, but an overlay update, as follows:

import React, { useState } from "react"; function Demo() { const [obj, setObject] = useState({ count: 0, name: "zxf" }); return ( <div className="App"> Count: {obj.count} <button onClick={() => setObject( { ... obj, count: obj.count + 1 } )}>+</button> </div> ); }Copy the code

useEffect

UseEffect The first argument is a function that returns a function that is used as a cleanup function for uninstallation, similar to the cleanup we do in componentDidMount. For the second argument, try not to use an empty array unless the effect really isn’t affected by other props, rather than “you think” it isn’t needed.

useContext

UseContext reduces component hierarchy. In class components, we want to use the data passed from the root element (such as the theme color), usually using layers of props or react. Context, and a provider wrapper:

const { Provider, Consumer } = React.createContext(null); Using useContext eliminates Consumer nesting, makes it easier to use, and makes code reading more brief:

const themeContext = React.createContext("summer");

function Bar() {
  const theme = useContext(themeContext);
  return <div>{theme}</div>;
}

function Foo() {
  return <Bar />;
}

function App() {
  return (
    <themeContext.Provider value={"spring"}>
      <Foo />
    </themeContext.Provider>
  );
}
Copy the code

useReducer

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

The useReducer, an alternative to useState, receives a Reducer of the form (state, action) => newState and returns the current state and its corresponding dispatch methods.

In which cases is useReducer recommended? For example, the state logic is complex and contains multiple child values (useState is an overridden update, as we mentioned above), or the next state depends on the previous state, etc. Use useReducer to write a counter example:

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();
  }
}

const Demo = ()=>{
  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

React ensures that the functions identified by Dispatch are stable and will not change due to re-rendering of the component in which they reside.

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

useCallback

UseCallback returns a mnemonized function:

const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], ); Function App() {const handleClick = () => {console.log('Click happened'); } return <SomeComponent onClick={handleClick}>Click</SomeComponent> }Copy the code

The above code, even if nothing is changed, is rerendered every time the page is rendered because of the transfer function. But using callback rewrite as follows:

Function App() {const memoizedHandleClick = useCallback (() => {console.log('Click happened'); },[]) return <SomeComponent onClick={memoizedHandleClick}> Click </SomeComponent>}Copy the code

It returns the Memoized version of the callback function, which is updated only when a dependency changes. UseCallback (fn, deps) is equivalent to useMemo(() => FN, deps).

UseMemo memory component

UseMemo is similar to useCallback, except that useCallback returns the first argument directly (without execution), whereas useMemo executes the first argument. We often wrap our function components in useMemo.

useRef

const refContainer = useRef(initialValue);
Copy the code

UseRef returns a mutable ref object whose.current property is initialized as the passed parameter (initialValue). The ref object returned remains constant throughout the life of the component. A common way to control a self-component is imperative:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus</button>
    </>
  );
}
Copy the code

Ref is a normal js object, so I just declare obj. No. UseRef () and create a {current:… The only difference with the} object is that useRef returns the same ref object every time it renders. If it is a normal object, it will be initialized every time it is rendered.

UseRef does not notify you when the ref object’s contents change. Changing the.current property does not cause component rerendering. If you want to run some code when React binds or unbinds the REF of the DOM node, you need to use the ref (useCallback) callback to do so.

Here’s a quick question for useState and useRef: When the user clicks the button and then enters something in the input field, will message be the same value as when the user clicked the button or the updated value 3 seconds later?

function MessageThread() {
  const [message, setMessage] = useState('');

  const showMessage = () => {
    alert('You said: ' + message);
  };

  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };

  const handleMessageChange = (e) => {
    setMessage(e.target.value);
  };

  return (
    <>
      <input value={message} onChange={handleMessageChange} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}
Copy the code

The result will pop input to modify the previous value.

The Value pops up inside the function component. This is the Capture Value feature in React Hooks. But if you use ref you get the latest value.

useImperativeHandle

UseImperativeHandle lets you customize the instance value exposed to the parent component when using the ref, and is usually used with the forwardRef:

function ChildInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(ChildInput);
Copy the code

The render parent can call inputref.current. Focus ():

function App() { const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); } []); return ( <div> <ChildInput ref={inputRef} /> </div> ); }Copy the code

Lastly, the hooks esLint plugin is recommended:

eslint-plugin-react-hooks. This ESLint plugin enforces the Rules of Hooks. It is a part of the Hooks API for React.

We recommend enabling the Strict-deps rule in eslint-plugin-react-hooks. This rule warns when you add false dependencies and suggests how to fix them.

over