This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
Today I’m going to share with you something important in React: Hooks. I’m going to use the following directory to analyze the basic use of Hooks in my project.
-
Existing problems
-
The purpose of the Hooks
-
Which are fixed by Hooks
-
Common Hooks
useState
useRender
useContext
useEffct
useCallback
useMemo
useRef
-
Customize the Hook
Problems with class components
Counter class
class Example extends Component{
constructor(props){
super(a);this.state={
count:0
}
}
addNum = () = >{
this.setState({
count:this.state.count + 1})}render(){
return(
<div>
<p>this num is {this.state.count}</p>
<button onClick={this.addNum}>click me</button>
</div>)}}Copy the code
Existing problems:
-
Use the bind or arrow function to constrain the scope of this in our function
-
State logic becomes difficult to reuse and complex components become difficult to understand
React also supports function components
function App(props) {
return <h1>Hello, {props}</h1>;
}
Copy the code
But there is a limitation. Pure functions have no state, no life cycle, and therefore cannot replace classes
The purpose of the Hooks
React Hooks are designed to enhance function components so that they can write a fully functional component without using “classes” at all
A hook is a way to hook in state and other React features
Use whatever hooks you need. React provides some common hooks by default, but you can also encapsulate your own.
All hooks introduce external functions to functions. Therefore, the React convention uses the use prefix to name hooks for easy identification.
Counter hooks
function Example (){
const [count,setCount] = useState(0);
return(
<>
<p>the num is {count}</p>
<button onClick={()= >{setCount(count+1)}}>click me </button>
</>)}Copy the code
Which are fixed by Hooks
-
A Hook can only be called from the outermost layer inside a function, not from a loop, a judgment, or a child function
-
You can only call a Hook in the React function component, not in a JavaScript function
Q: Why can only be called from the outermost layer inside a function?
The reason:
Multiple hooks are written in a single component, and the order in which they are called is determined by where they are written. Its internal implementation is actually a linked list of calls. There is no guarantee that it will work properly if you can’t make sure that every render is in the same position.
Common Hook
Examples in projects
importReact, {useState, useEffect, useCallback}from 'react';
function APP() {
const [refreshCount, setRefreshCount] = useState<number>(0)
const [list, setList] = useState<ClusterDO[] | null> ([])const handleUpdate = useCallback(
() = > setRefreshCount((prevState) = > prevState + 1),
[])
useEffect(() = > {
const fetchData = async() = > {const result = await clusterInfo(uniqueId)
if (result.success) {
setList(result.data)
} else {
message.error(result.message)
setList(null)
}
}
fetchData()
}, [uniqueId, refreshCount])
return (
<>
<Table dataSource={list} />
<button onClick={handleUpdate}>
Refresh
</button>
</>
);
}
Copy the code
uesState
// 1. Introduce React and useState
import React, { useState } from 'react';
function APP() {
// 2. Declare state variable
const [refreshCount, setRefreshCount] = useState<number>(0)
const [list, setList] = useState<ClusterDO[] | null> ([])//3. Update variables
const handleUpdate = () = > setRefreshCount((prevState) = > prevState + 1)
return (
<>//4. Use the defined variables<p>{refreshCount}</p>
<button onClick={handleUpdate}>
Refresh
</button>
</>
);
Copy the code
- Why call
useState
? useState()
What is the return value of? What’s the use?- Multiple calls
useState
.React
If you know that everyuseState
What value does that correspond to?
Why call useState?
- call
useState
I defined onestate
States can be used as variables. This isThe way a function holds variablesAll other variables disappear after the function exits, butstate
Will be preserved
What is the return value of useState()? What’s the use?
-
Calling useState returns an array of items: a state and a function that updates the state
-
During initial rendering, the state returned is the same as the value of the first parameter passed in
-
We can call this update function in an event handler or some other place, and we can change the value of state by passing a new value to the update function, and it will just replace the old state with the new state
React if you know what value each useState corresponds to?
- Chain calls
tips
Use multiple state variables (separate separate states)
-
UseState also supports passing objects and arrays, so it is possible to write multiple variables into an object. However, since state updates replace its values rather than merge them, it is recommended that multiple variables be defined separately and changed simultaneously. Another advantage of writing this way is that the two variables can be extracted more easily in different components
// It is not recommended to write all states in one object like this const [state, setState] = React.useState({ isFetching: false.startTime: null.endTime: null,})// Update one of the methods setState((prevState) = > ({ ...prevState, isFetching: false })) Copy the code
Problems encountered
! [image-20210712235354343](/Users/admin/Library/Application Support/typora-user-images/image-20210712235354343.png)
-
Why is the output of the setState function unchanged immediately after execution
-
The value of state.selectedrowKeys for the next click will still be empty. Why is it not the value that was set last time?
-
Why does the render state.selectedrowkeys output have the latest value?
Why is the output of the setState function unchanged immediately after execution
Answer: The update status method returned by useState is “asynchronous” and will not get the new value until the next redraw. Do not try to get the state immediately after changing it
The value of state.selectedrowKeys for the next click will still be empty. Why is it not the value that was set last time?
A: The value state.selectedRowKeys is not added to the useCallback dependency, so it will always be the first value, but the first render value will be empty
Why does the render state.selectedrowkeys output have the latest value?
A: The log is synchronous and gets the latest value during re-rendering
Classic interview questions
-
In the react
setState
Is it synchronous or asynchronous? When is it asynchronous, can it be synchronous, and when is it synchronous?-
SetState is “asynchronous” only in synthesized events and hook functions, and synchronous in both native events and setTimeout.
-
The “asynchronous” of setState does not mean that it is internally implemented by asynchronous code, in fact, the process and code itself are synchronous, but the call order of the synthesized event and hook function is before the update, so that the updated value cannot be immediately obtained in the synthesized event and hook function, which forms the so-called “asynchronous”. Of course, the callback in the second parameter setState(partialState, callback) can get the updated result.
-
The batch update optimization of setState is also based on “asynchronous” (composite event, hook function). Batch update will not be performed in the native event and setTimeout. In “asynchronous”, if setState is performed for the same value for many times, the batch update strategy of setState will overwrite it. Take the last execution, if setState is multiple different values at the same time, it will be merged during the update batch update.
tips
React Is an event object that simulates the functionality of native DOM events. It is a cross-browser wrapper for browser native events. It is defined by W3C standards, is compatible with all browsers, and has the same interface as native events
React all events are synthesized and are not native DOM events. However, native DOM events can be obtained by using e.ativeEvent
// Native DOM events <button onclick="activateLasers()">Activate Lasers</button> //React synthesizes events <button onClick={activateLasers}>Activate Lasers</button> Copy the code
Characteristics of composite events
- Name in camel case, not all lowercase
- Using JSX syntax requires passing in a function as an event handler, not a string
-
useReducer
It is an alternative to useState. It receives a Reducer of the form (state, action) => newState and returns the current state and its accompanying dispatch method.
const [state, dispatch] = useReducer(reducer, initialArg, init)
Copy the code
Usage scenarios
UseReducer can be more useful than useState in some situations, such as when the state logic is complex and contains multiple subvalues, or when the next state depends on the previous state.
Also, using useReducer can optimize performance for components that trigger deep updates because you can pass dispatches to child components instead of callbacks
An example of rewriting the useState counter
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
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
tips
Specify initial state: The initial value is passed in as the second argument
const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
)
Copy the code
Lazy initialization: The function created is passed in as the third argument to the useReducer. The initial state is the argument to the function
Doing so extracts the logic used to calculate the state outside the Reducer, which also facilitates future actions to reset the state
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);
return (
<>
Count: {state.count}
<button// Reset buttononClick={()= > dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={()= > dispatch({type: 'decrement'})}>-</button>
<button onClick={()= > dispatch({type: 'increment'})}>+</button>
</>
);
}
Copy the code
Skip Dispatch (Idempotence of useReducer)
If the Reducer Hook returns the same value as the current state, React skips the rendering of the child components and the execution of the side effects. (React uses the Object.is comparison algorithm to compare states.)
useEffect
-
UseEffect can perform side effects in function components, most commonly fetching background data
-
UseEffect Hook can be regarded as the combination of three life cycle functions componentDidMount, componentDidUpdate and componentWillUnmount
useEffect(() = > { const fetchData = async() = > {const result = await clusterInfo(uniqueId) if (result.success) { setList(result.data) } else { message.error(result.message) setList(null) } } fetchData() }, [uniqueId, refreshCount]) // Only re-render when the page is first rendered and the uniqueId or refreshCount changes Copy the code
UseEffect () is executed whenever the array changes. The first argument is a function that can hold asynchronous operations, and the second argument is an array that holds dependencies
-
UseEffect can be classified into an effect that needs to be cleared and an effect that does not need to be cleared
Actions that don’t need to be cleaned: After React updates the DOM, some extra code is run, such as sending network requests, manually updating the DOM, and printing logs, which don’t need to be cleaned
Operations to clear: subscribes to external data sources. Clearing prevents memory leaks
How to implement cleanup?
- if
effect
Returns a function,React
It will be called when the cleanup is performed
useEffect(() = > { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Implement cleanup return () = > { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; },[props.friend.id] Copy the code
- call
useEffect
What did you do? useEffect
Does it render every time?- When the purge will be performed
effect
?
- if
What does the call to useEffect do?
- Through this
hook
Can you tellReact
The actions that the component will perform after rendering,React
Will save the function passed inDOM
Call after update.
Will useEffect render every time?
useEffect
It is executed on the first render and every update, and what controls it is that it is triggered when the variables in the second argument list are updated
When a clean effect is performed
- During component uninstallation
React
A cleanup operation is performed
tips
Use effects with separate logic
Using multiple effects to separate different logic into different effects allows for separation of concerns, and React calls each Effect in the component in the order in which it is declared.
useCallback
const memoizedCallback = useCallback(
() = > {
doSomething(a, b);
},
[a, b],
);
Copy the code
Returns a Memoized callback function.
Passing the inline callback function and an array of dependencies as arguments to useCallback returns the version of the callback function that is updated only when a dependency changes.
Use reference equality to avoid unnecessary rendering when you pass callback data to the optimized
The dependency array is not passed as an argument to the callback function. All values referenced in the callback function should appear in the dependency array.
UseCallback (fn, deps) equivalent to useMemo(() => fn, deps)
The difference between useMemo and useCallback is that useMemo caches the return value of a function, while useCallback caches the function itself. Both apis are performance optimization methods
Memoization is an optimization technique primarily used to speed up program computations by allowing functions to avoid repeating calculations of previously processed inputs and return cached results
useMemo
const memoizedValue = useMemo(() = > computeExpensiveValue(a, b), [a, b]);
Copy the code
Returns an memoized value.
You pass in the create function and the dependency array as arguments to useMemo, which recalculates memoized values only when a dependency changes.
This optimization helps avoid costly calculations every time you render.
Functions passed into useMemo are executed during rendering.
You can put theuseMemo
As a means of performance optimization, but not as a semantic guarantee.
useRef
UseRef returns a mutable ref object whose.current property is initialized as the passed parameter (initialValue).
const refContainer = useRef(initialValue)
Copy the code
The ref object returned remains the same throughout the life of the component, meaning that the same ref object is returned each time the function component is re-rendered (using react.createref, refs are recreated each time the component is re-rendered)
Usage Scenarios:
3. UseRef is used as a variable, and modifying it does not render
function TextInputWithFocusButton() {
const echEL = React.useRef(null)
React.useEffect(() = > {
constmyChart = echarts.init(echEL.current) .... And []})return (
<div ref={echEL} className="echartsSty"></div>
);
}
Copy the code
useContext
Prior knowledge: Context
Context is a way to pass data across the component tree without manually adding Props for each layer of components
Role: Share global data for a component tree.
Main application scenario: Many components at different levels need to access the same data. Use caution because this makes components less reusable.
UseContext takes a context object and returns the current value of that context. The current value of the current context. The current context value is determined by the < MyContext.provider > value prop of the upper-layer component closest to the current component.
When the most recent < myContext. Provider> update is made to the component’s upper layer, the Hook triggers a rerender and uses the latest context value passed to the Provider.
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(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background.color: theme.foreground}} >
I am styled by theme context!
</button>
);
}
Copy the code
Custom Hooks
State logic between shared components can use custom hooks (or render props and higher-order components). Custom hooks are a function that starts with use and calls other hooks from within a custom Hook
export function useClusterDetail(uniqueId: string) :ClusterDetailResult {
const [cluster, setCluster] = React.useState<ClusterDO | null> (null)
const [refreshCount, setRefreshCount] = React.useState<number>(0)
React.useEffect(() = > {
const fetchData = async() = > {const result = await clusterInfo(uniqueId)
if (result.success) {
setCluster(result.data)
} else {
$notification.error(result.message)
setCluster(null)
}
}
fetchData()
}, [uniqueId, refreshCount])
return { cluster, setRefreshCount }
}
/ / use
const { cluster, setRefreshCount } = useClusterDetail(uniqueId)
Copy the code
The above are some of my React Hook usages in the project, as well as the pitfalls I usually encounter. Later, I will also explore and share some advanced usages. If it is helpful to you, please give me a thumbs up or follow me and grow with me.