1, Why learn hooks, what are hooks, what do hooks do?
Hook is a new feature in Act 16.8. It is a special function that allows you to “Hook” into the React feature and use state and other React features without having to write a class component. Why hook? As we develop, we often encounter problems such as difficulties in reusing state logic between components, complex components that become difficult to understand, classes that are difficult to understand, and so on. Hooks reuse state logic without modifying the component structure. Hooks break down the interrelated parts of a component into smaller functions (such as setting up subscriptions or requesting data) rather than enforcing partitioning by lifecycle. Reducer can also be used to manage the internal state of components to make them more predictable. Hooks can also use more React features without using class.
2. An overview of HOOKS
1, use,useState
import React, { useState } from 'react';
function Example() {
// Declare a state variable called "count".
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>{/* use State */}<button onClick={()= >SetCount (count + 1)}> {/* update state*/} Click me</button>
</div>
);
}
Copy the code
The only argument to useState is the initial state, in this case useState(0), where 0 is the initial state. UseState returns a pair of values: the current state (count) and a function to update it (setCount). You can call this function from an event handler or some other place. It is similar to the this.setState of the class component, but it does not merge the new state with the old state. Here is an example of an equivalent class hook implementation:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>{/* use State */}<button onClick={()= >This.setstate ({count: this.state.count + 1})}> {/* Update state*/} Click me</button>
</div>); }}Copy the code
In class, by setting this.state to {count: 0} to initialize count state to 0. When displaying the current state, read this.state.count. When changing state, in class, this.setstate () is required to update the count value. In the function, we already have setCount and count variables, so we don’t need this
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
Copy the code
There is no this in the function component, so this.state cannot be assigned or read. You can call useState Hook directly from a component. UseState Hook can be used multiple times in a component. Multiple variables can be created at the same time.
import React, { useState } from 'react';
function Example() {
// Declare a state variable called "count"
const [count, setCount] = useState(0);
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
Copy the code
Note: What do I do when I call the useState method? It defines a variable count, which does exactly the same thing as this.state in class. Normally, the variable “disappears” after the function exits, while variables in state are retained by React. The only argument in useState() is the initial state. Unlike class, you do not have to use an object. You can use numbers or strings as required. What is the return value of the useState method? Const [count, setCount] = useState() const [count, setCount] = useState() The only difference is that you need to get them in pairs. So the example above can be interpreted as: declare a state variable called count and set it to 0. React remembers its current value during repeated rendering and provides the latest value to the setCount function, which updates the current count by calling setCount.
2, use,useEffect
UseEffect Hook is a combination of componentDidMount componentDidUpdate and componentWillUnmount. Unlike componentDidMount or componentDidUpdate, effects scheduled with useEffect do not block the browser update screen, making the application appear more responsive. In most cases, effects do not need to be executed synchronously. UseEffect can perform side effects on function components (fetching data, setting subscriptions, and manually changing the DOM in the React component are all side effects).
/ / class components
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `You clicked The ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked The ${this.state.count} times`;
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={()= > this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>); }}// Function components
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// componentDidMount and componentDidUpdate:
useEffect(() = > {
// Update document titles using browser apis
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()= > setCount(count + 1)}>
Click me
</button>
</div>
);
}
Copy the code
Note: In a Class component, you write duplicate code in two lifecycle functions. With useEffect, the component is told to do something after rendering, save it, and call it after performing a DOM update. Why call inside a function component? Because putting useEffect inside the component allows us to access the count State variable (or other props) directly in effect, the Hook uses JavaScript’s closure mechanism without introducing a specific React API to read it. By default, useEffect is executed after the first rendering and after every update.
UseEffect Hook (componentDidMount componentWillUnmount) useEffect Hook (componentDidMount componentWillUnmount) useEffect Hook (componentDidMount componentDidUpdate) The update logic doesn’t need special code to handle it, because useEffect does it by default, cleaning up the previous effect before calling a new one. Multiple effects can be used to separate unrelated logic into different effects,
useEffect(() = > {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () = > { // Clean up
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// Performance optimization
useEffect(() = > {
document.title = `You clicked ${count} times`;
}, [count]); // Update only when count changes
Copy the code
3. Use useContext
const value = useContext(MyContext); Receive a context object (the return value of React. CreateContext) and return the current value of the context, as determined by the value prop of the upper component < myContext.provider > closest to the current component. When the most recent < myContext. Provider> update occurs on a component, useContext triggers a rerender and passes the latest value to the context value of MyContext Provider, as in the following example:
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() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background.color: theme.foreground}} >
I am styled by theme context!
</button>
);
}
Copy the code
4. Use useReducer
Const [state, dispatch] = useReducer(Reducer, initialArg, init) is an alternative to useState. When state logic is complex and contains multiple subvalues, If the next state depends on the previous state, useReducer is more useful than useState, and useReducer can optimize the performance of components that will trigger deep updates.
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);
// The third argument is used for lazy initialization
return (
<>
Count: {state.count}
<button
onClick={()= > dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={()= > dispatch({type: 'decrement'})}>-</button>
<button onClick={()= > dispatch({type: 'increment'})}>+</button>
</>
);
}
Copy the code
5. Use useCallback
Passing the inline callback function and an array of dependencies as arguments to useCallback returns the current version of the function, which is updated only when a dependency changes
const memoizedCallback = useCallback(
() = > {
doSomething(a, b);
},
[a, b],
);
Copy the code
Note: The dependency array is not passed as an argument to the callback function. Conceptually, though, it looks like this: all values referenced in callback functions should appear in dependency arrays.
6. Use useMemo
Const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]) It recalculates the Memoized value only when a dependency changes. This optimization helps avoid costly calculations every time you render. Functions passed into useMemo are executed during rendering. Do not perform operations that are not related to rendering inside this function. If you do not provide an array of dependencies, useMemo will evaluate new values every time it renders. UseCallback (fn, deps) is equivalent to useMemo(() => FN, deps).
6. Use useRef
Const refContainer = useRef(initialValue)useRef returns a mutable ref object whose.current property is initialized as the parameter passed in (initialValue). The ref object returned remains constant throughout the life of the component. Pass the ref object to the component as
, and React sets the.current property of the ref object to the corresponding DOM node, regardless of how the node changes. UseRef () is more useful than the ref attribute, which makes it easy to store any mutable values because the ref attribute creates a normal Javascript object. UseRef () and create a {current:… The only difference with the} object is that useRef returns the same ref object every time it renders.
function TextInputWithFocusButton() {
const myRef = useRef(null); // null is the initial value
const onButtonClick = () = > {
// 'current' points to the text input element mounted to the DOM
myRef.current.focus();
};
return (
<>
<input ref={myRef} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Copy the code
7. Use useImperativeHandle
UseImperativeHandle (ref, createHandle, [deps])useImperativeHandle can customize the instance value exposed to the parent component when using ref. In most cases, imperative code like ref should be avoided. UseImperativeHandle should be used with the forwardRef:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () = > ({
focus: () = >{ inputRef.current.focus(); }}));return <input ref={inputRef} . />;
}
FancyInput = forwardRef(FancyInput);
The parent component rendering
can call inputref.current.focus().
Copy the code
8. Use useLayoutEffect
The function participates in useEffect the same way, but it calls effect synchronously after all DOM changes. You can use it to read DOM layouts and trigger rerenders synchronously. The update schedule inside useLayoutEffect is refreshed synchronously before the browser performs the drawing.
9. Use useDebugValue
UseDebugValue can be used to display tags for custom hooks in the React developer tool. For example, a custom Hook that returns a Date value can be formatted to avoid unnecessary toDateString calls: useDebugValue(Date, Date => date.todateString ());