WHY
1.React does not provide reusable behavior for components
Class components are a solution to this problem: higher-order components
- HOC An advanced technique used in React to reuse component logic. Not part of the React API, it is a design pattern based on the composite features of React.
- Specifically, a higher-order component is a function that takes a component and returns a new component.
- A component converts props to UI, and a higher-order component converts a component to another component. HOC is common in third-party libraries for React, such as Redux’s Connect and Relay’s createFragmentContainer
- You need to reorganize the structure of components.
- Observe React applications in React DevTools. Components composed of providers, consumers, high-order components, render props, and other abstraction layers form a nested hell.
- Although we can filter them out in DevTools, this illustrates a deeper problem: React needs to provide a better native way to share state logic. React DevTools Online simulation
The class component solves this problem by using hooks to extract state logic \ from the component
- This logic can be individually tested and reused
- Reusing state logic without modifying component structure
- It’s easy to share hooks between components or within a community
Using class components unintentionally encourages developers to use solutions that make optimizations ineffective, and class also creates problems for current tools. LLDB: class does not compress well and makes thermal overload unstable. Therefore, the functional formula is recommended
3. One purpose of using Hook is to aggregate functional modules and not to be separated by life cycle functions
- For example, the mapping between useState implementation values and setState
- For example, Effect solves the problem that life cycle functions in a class often contain unrelated logic but separate the related logic into several different methods.
Functional component
Functional components have no concept of instances; functions are rendered by execution
// Function component 1.0
const Example = (props) = > {
// You can use Hook here
return <div />;
}
// Function component 2.0
function MyFunctionalComponent() {
// You can use Hook here
return <input />;
}
Copy the code
Hook API
- React 16.8 is a new feature
- The Hook API provides a set of reusable logic code for embeddable functional components, allowing developers to use state in function components and other React Class components. Hooks names usually begin with use
- When the React function component is rendered, each line of code in the component is executed in sequence. In this case, Hooks are called and executed in sequence.
Requirements:
- A Hook can only be called on the outermost layer of a function. Do not call in loops, conditional judgments, or subfunctions.
- You can only call a Hook in the React function component. Do not call it from another JavaScript function. Can be called in a custom Hook
First, basic life cycle correlation
useState
const[state, setState] = useState(initialState); If initialState needs to be obtained through a complex calculation, you can pass in a function that calculates and returns the initialState, which is called only during the initial renderCopy the code
- The setState function is used to update state by receiving a new state value and enqueuing a re-rendering of the component.
- React ensures that the setState function identifier is stable and does not change when the component is re-rendered.
SetState Synchronous or asynchronous
When is setState in React synchronous and when is it asynchronous?
- Calls to setState in events outside of React control are updated synchronously. Things like native JS bindings, setTimeout/setInterval, etc.
- The React controlled event handler, as well as the lifecycle function call setState, updates the state asynchronously.
- In the React setState implementation, the variable isBatchingUpdates determines whether to update this.state directly or asynchronously in a queue
- IsBatchingUpdates default to false to synchronize this.state. However, there is a function called batchedUpdates that sets isBatchingUpdates to true.
- When React calls batchedUpdates before the event handler is called, setState controlled by React does not update this.state synchronously.
The setState function is passed in as a value
- Value: Optimized for one execution within 3 seconds in the same asynchronous queue
- Function: keeps the execution of each handleClick triggered
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setTimeout(() = > {
setCount(count + 1)},3000);
}
function handleClickFn() {
setTimeout(() = > {
setCount((prevCount) = > {
return prevCount + 1})},3000);
}
return (
<>
Count: {count}
<button onClick={handleClick}>+</button>
<button onClick={handleClickFn}>+</button>
</>
);
}
Copy the code
useEffect
- A rerendering of a functional component is a function execution process. With this Hook, React saves the passed function and calls effect periodically.
- UseEffect Hook functions equal to a combination of componentDidMount, componentDidUpdate, and componentWillUnmount
Unlike componentDidMount or componentDidUpdate, effects scheduled with useEffect don’t block the browser update screen, which makes the application seem more responsive.
- By default, useEffect is executed after the first rendering and after every update (equivalent to using componentDidMount and componentDidUpdate at the same time), and it can also be triggered with a second parameter.
- React ensures that the DOM is updated every time an Effect is run, and Effect handles side effects
- In most cases, effects do not need to be executed synchronously. In individual cases (such as measuring layout), there is a separate useLayoutEffect Hook for you to use, with the same API as useEffect.
- UseEffect is placed inside a component to access the count State variable (or other props) directly in effect without requiring a special API to read it. Already saved in function scope.
In contrast to JavaScript classes where methods are not bound to this, hooks use JavaScript’s closure mechanism
Side effects
Side effects: data fetching, setting up subscriptions, and manually changing the DOM in the React component
- For example: Run some extra code after React updates the DOM. Such as sending network requests, manually changing the DOM, and logging
- Classification of side effects operations: those that need to be removed and those that do not.
Effect that does not need to be cleaned
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() = > {
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
Effects that need to be cleared
- Effect Optional cleanup mechanism: If your effect returns a function, React will call it when the cleanup operation is performed, so that the logic for adding and removing subscriptions can be put together.
- React cleans up the previous effect before executing the current effect.
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() = > {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading... ';
}
return isOnline ? 'Online' : 'Offline';
}
Copy the code
Why frequently unsubscribes to last Effect
- Why is the effect cleanup phase performed every time you re-render, instead of just once when you unload the component?
- This default behavior ensures consistency and avoids common bugs in class components that do not handle update logic.
Forgetting to properly handle componentDidUpdate is a common source of bugs in React applications. For example, let’s say we have a ChatAPI module that allows us to subscribe to our friends’ online status.
class FriendStatus extends React.Component {
constructor(props) {
super(props);
this.state = { isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
render() {
if (this.state.isOnline === null) {
return 'Loading... ';
}
return this.state.isOnline ? 'Online' : 'Offline'; }}Copy the code
Skip the Effect
- Pass an array as the second optional argument to useEffect
- If you want to execute effect once (only when the component is mounted and unmounted), you can pass an empty array ([]) as the second argument
useEffect(() = > {
document.title = `You clicked ${count} times`;
}, [count]); // Update only when count changes
useEffect(() = > {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () = > {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]); // Re-subscribe only when props.friend.id changes
Copy the code
B. custom hooks
When the render props and higher-order components want to share logic between two functions, we extract it into a third function. Components and hooks are functions, so the same applies
- When we want to share logic between two functions, we extract it into a third function. Components and hooks are functions, so component logic can be extracted into reusable functions in the same way.
- Custom hooks. The name of a custom Hook should always start with use.
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() = > {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () = > {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
Copy the code
3. Data management
Share data useContext
- Global: ThemeContext = react.createcontext (semantic object)
- Global: value of themecontext. Provider specifies the injected value
- Underlying use: useContext(ThemeContext)
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
React.useReducer + react. useContext
Handle data useReducer(an alternative to useState)
Compared with useState, it supports better method allusion to set data values and is suitable for more complex data processing scenarios.
- UseState: Data processing is controlled by users themselves
- UseReducer: The useReducer defines data processing (Reducer). The Reducer calls the corresponding methods by passing a type value
State: read-only (all changes must be made using action) DomainData Server response data, network request UI APP level Action: a JS object with type attribute and other attributes reducer: Receive functions that initialize state and action, respond to different types of action, modify and return state to send to storeObject.assign({},newstate)
Copy the code
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
Cache optimization is related to useCallback and useMemo
- UseCallback, useMemo, and useEffect can be set to recalculate only after dependent data changes, serving as a cache
- Not using Memo and useMemo should not cause your business logic to change (Memo and useMemo are only used for performance optimization), similar to shouldComponentUpdate in class components
- If this function or variable is passed to a child component as props, be sure to use it to avoid unnecessary rendering of the child component, like PureComponent
- The second argument is used to trigger – detect whether the corresponding value in the context has changed, and if so redeclare the callback function to get the value of the static scope.
- If the second argument is an empty array, it will only run when component didMount. If this parameter does not exist, it is run on each render.
- React.memo and react. useCallback need to be used together. If either of them is missing, the performance will drop rather than increase.
UseMemo cache value
UseMemo (calculation function, dependency array)Copy the code
- The execution timing of useMemo and useEffect is different:
UseMemo cache calculation status. The return value is directly involved in rendering, so useMemo is done during rendering and useEffect is performed after rendering.
- Evaluation functions are performed during rendering. Please do not perform non-rendering operations inside this function. Operations such as side effects are used by useEffect, not useMemo
const memoizedValue = useMemo(() = > computeExpensiveValue(a, b), [a, b]);
Copy the code
UseCallback cache function (cache function scope, reduce the performance cost of function creation)
The inline function
- Since onClick uses inline functions, the PureComponent’s default shallow comparison is also meaningless.
- When this callback function is passed as a prop to child components, they may undergo additional re-rendering.
- UseCallback caches an instance of the inline callback for each render
<button onClick={() => this.handleClick()}>
Click me
</button>
Copy the code
const handleClick = useCallback(
(value: any) = > {
const targetOption: any = options.find(o= > o.value === value);
if (targetOption) {
setInstantEditing(targetOption.data);
}
},
[options],
);
Copy the code
Five, the other
useRef
function TextInputWithFocusButton() { const inputEl = useRef(null); Const onButtonClick = () => {// 'current' points to the text input element inputel.current.focus () mounted to the DOM; }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }Copy the code
- When the ref object’s contents change,
useRef
You will not be notified - Assigning a callback function to ref informs you when the contents of the REF object change. For example, measure DOM requirements
Measuring the DOM
function MeasureExample() { const [height, setHeight] = useState(0); const measuredRef = useCallback(node => { if (node ! == null) { setHeight(node.getBoundingClientRect().height); }} []); return ( <> <h1 ref={measuredRef}>Hello, world</h1> <h2>The above header is {Math.round(height)}px tall</h2> </> ); }Copy the code