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.
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
The renderFruit value is recalculated every time we click on the button. You can open the browser console to see the log.
Now we use useMemo
hook to rewrite the above example.
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:
- 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).
- 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 usingref
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} />;
};