Skip to main content

Deep know about React Hook, this article is enough.

· 7 min read

Today, I will show you what is a react hook?

What is a react hook?

Hooks solve a wide variety of seemingly unconnected problems in React that we’ve encountered over five years of writing and maintaining tens of thousands of components. Whether you’re learning React, use it daily, or even prefer a different library with a similar component model, you might recognize some of these problems.

hooks solve these problems:

  • It’s hard to reuse stateful logic between components
  • Complex components become hard to understand
  • Classes confuse both people and machines

useState

useState 和 Class Component 中的 this.setState 类似。

When we define the state with useState, it returns an array of two values. The first value is the current state, and the second value is the function that updates the state.

Live Editor
Result
Loading...

Click the p tag and click three times quickly Click Me button! At this point, the value of count has been updated to 3 on the page, but it will still print 0 in setTimeout after 3s. Why ?

Each render function has its own props and state. When rendering in JSX code using state, each render gets props and state in its own render scope.

When the setTimeout fires, the count is taken because the closure is the internal count value when the function was first rendered. Alert is 0.

Batch update principle

Asynchronous batch means that if multiple state changes are involved in a page update, the result of multiple state changes will be merged to get the final result and then a page update will be carried out.

when async batch?

  • SyntheticEvent
  • lifecycle function that synchronizes code within a component

when sync batch?

  • setTimeout
  • setInterval
  • Source DOM events.

useEffect

useEffect is called a side effect hook. useEffect allows you to perform side effects in function components.

useEffect hook supports two arguments. The first argument is a function representing the side effect function, which is executed by default after the first rendering and after every update.

The second argument is an array that specifies the dependencies of the first argument (the side effect function). The side-effect function is executed only if the variables in the array change.

useUpdateEffect

There is a componentDidUpdate lifecycle in the Class component. It will be called immediately after the update and will not be executed for the first rendering.

In function component we can use the useEffect wrapper to implement componentDidUpdate

We can implement a Hook using useRef to determine if it is the first rendering.

// useFirstMountState.tsx
import { useRef } from "react";

export function useFirstMountState(): boolean {
const isFirst = useRef(true);

if (isFirst.current) {
isFirst.current = false;
return true;
}

return isFirst.current;
}

Now we can use the useFirstMountState hook to determine if it is the first rendering.

import { useEffect } from "react";
import { useFirstMountState } from "./useFirstMountState";

const useUpdateEffect: typeof useEffect = (effect, deps) => {
const isFirstMount = useFirstMountState();

useEffect(() => {
if (!isFirstMount) {
return effect();
}
}, deps);
};

export default useUpdateEffect;

useMemo

useMemo can be thought of as "memorizing" values for performance optimization.

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

The first argument takes a function passed in, and the return value of the function call passed in is "remembered". It is only when the dependency changes that the function passed in is re-evaluated for the new return.

The second argument is also an array that represents the dependencies corresponding to the first argument.

Let's take an example

Live Editor
Result
Loading...

The renderFruit value is recalculated every time we click on the button. You can open the browser console to see the log.

image.png

Now we use useMemo hook to rewrite the above example.

Live Editor
Result
Loading...

As we can see, no matter how re-render the component is, the value returned in useMemo will not be recalculated as long as the dependency does not change. because of the dependency is an empty array.

useImperativeHandle

useImperativeHandle is a hook that may not use very much in daily life, but in some cases, it can help us achieve some unintended effects.

useImperativeHandle(ref, createHandle, [deps]);

Usually when you use useRef you are given the instance value of the component the ref is attached to. This allows you to interact with the DOM element directly.

useImperativeHandle is very similar, but it lets you do two things:

  1. It gives you control over the value that is returned. Instead of returning the instance element, you explicitly state what the return value will be (see snippet below).
  2. It allows you to replace native functions (such as blur, focus, etc) with functions of your own, thus allowing side-effects to the normal behavior, or a different behavior altogether. Though, you can call the function whatever you like.

There could be many reasons you want might to do either of the above; you might not want to expose native properties to the parent or maybe you want to change the behavior of a native function. There could be many reasons. However, useImperativeHandle is rarely used.

useImperativeHandle customizes the instance value that is exposed to parent components when using ref

In this example, the value we'll get from the ref will only contain the function blur which we declared in our useImperativeHandle. It will not contain any other properties (I am logging the value to demonstrate this). The function itself is also "customized" to behave differently than what you'd normally expect. Here, it sets document.title and blurs the input when blur is invoked.

const MyInput = React.forwardRef((props, ref) => {
const [val, setVal] = React.useState("");
const inputRef = React.useRef();

React.useImperativeHandle(ref, () => ({
blur: () => {
document.title = val;
inputRef.current.blur();
},
}));

return <input ref={inputRef} val={val} onChange={(e) => setVal(e.target.value)} {...props} />;
});

const App = () => {
const ref = React.useRef(null);
const onBlur = () => {
console.log(ref.current); // Only contains one property!
ref.current.blur();
};

return <MyInput ref={ref} onBlur={onBlur} />;
};