React Hook
Emerging background
Class component
- Difficult to reuse component state, high order component + render properties
providers customers
A bunch of tools are designed to solve this problem, but create a hell of a cost of understanding and component nesting - Life cycle brings negative effects and logic splits seriously
- This points to the problem
Limitations of functional components
- There was no function component before
state
And life cycle, resulting in limited use scenarios
React Hook
Hooks are a new feature added in React 16.8 that lets you use state and other React features without having to write classes, without converting them to class components
Hook
The use and practice of
useState
和 Hook
The closure mechanism of
/ / hook component
function Counter() {
const [count, setCount] = useState(0);
const log = () = > {
setCount(count + 1);
setTimeout(() = > {
console.log(count);
}, 3000);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={log}>Click me</button>
</div>
);
}
// Equivalent class component
class Counter extends Component {
state = { count: 0 };
log = () = > {
this.setState({
count: this.state.count + 1});setTimeout(() = > {
console.log(this.state.count);
}, 3000);
};
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.log}>Click me</button>
</div>); }}Copy the code
In the case of a quick click, think Hook components and functional components console print out what?
- Class component prints out
3 3 3
Class
The component’sstate
Is immutable throughsetState
Returns a new reference,this.state
Point to a new referencesetTimeout
When executing, passthis
Get the lateststate
Reference, so this output is all3
- The output of the function component is
0 1 2
Closure mechanism for function components. Function components have independent closure for each renderingprops
和state
Each rendering has its own event handler
The state of each render is not affected by subsequent event processing
Function component rendering disassembly
Since each rendering is a separate closure, you can try code dismantling the rendering process for functional components
// First click
function Counter() {
const [0, setCount] = useState(0);
const log = () = > {
setCount(0 + 1);
// Only get the state of the button that was clicked this time
setTimeout(() = > {
console.log(0);
}, 3000);
};
}
// Second click
function Counter() {
const [1, setCount] = useState(0);
const log = () = > {
setCount(1 + 1);
setTimeout(() = > {
console.log(1);
}, 3000);
};
}
// Click the third time
function Counter() {
const [2, setCount] = useState(0);
const log = () = > {
setCount(2 + 1);
setTimeout(() = > {
console.log(2);
}, 3000);
};
}
Copy the code
-
Three clicks, four render times, and the count changes from 0 to 3
-
The first time the page is rendered, the page sees count = 0
-
On the first click, the event handler gets count = 0, and the count becomes 1. On the second rendering, the page sees count = 1 after rendering, which corresponds to the first click of the above code
-
The second click, the event handler gets the count = 1, the count becomes 2, the third rendering, the page after rendering, the count = 2, corresponding to the above code second click
-
On the third click, the event handler gets the count = 2, and the count becomes 3. On the fourth render, the page sees the count = 3, corresponding to the above code on the third click
Let functional components also output3 3 3
A simpler solution to the problem is to borrow useRef
useRef
Returns a variableref
Object, itscurrent
Properties are initialized to the parameters passed inThe initialValue ()
useRef
The returnedref
Object remains the same throughout the lifetime of the component, that is, each time the function component is rerenderedref
The object is the sameuseRef
This is analogous to the instantiation of a class componentthis
The returned reference is the same when the component is not destroyed
function Counter() {
const count = useRef(0);
const log = () = > {
count.current++;
setTimeout(() = > {
console.log(count.current);
}, 3000);
};
return (
<div>
<p>You clicked {count.current} times</p>
<button onClick={log}>Click me</button>
</div>
);
}
Copy the code
- With this modification, the console output is indeed
3 3 3
- ? since
Ref
The object remains the same throughout its lifetimecurrent
Properties are just modifying properties, except for printing, hereYou clicked 0 times
If you click it three times, it becomes3
? - Obviously not, this component does not have any properties or state changes, so it will be rerendered, so click here
3
Times, but not likeuseState
Same thing, render4
Second, this will only render1
And then all I see isYou clicked 0 times
- Fixing one problem introduces a bigger problem, which is very programmers…
useEffect
Although useRef can solve the problem of printing, but the page rendering is not right, here we still use useState solution, with useEffect can achieve the effect we want
function useEffect(effect: EffectCallback, deps? : DependencyList) :void;
Copy the code
- Look at the
useEffect
The signature,effect
Is a function type and is required. There is a second optional argument of type read-only array useEffect
Is to deal with side effects, which are executed at each timeRender
After rendering, in other words each rendering will be performed in realDOM
After the operation is completed.
With this hook, if the value in ref is updated after rendering after each state change, then the console prints the value of ref
function Counter() {
const [count, setCount] = useState(0);
const currentCount = useRef(count);
useEffect(() = > {
currentCount.current = count;
});
const log = () = > {
setCount(count + 1);
setTimeout(() = > {
console.log(currentCount.current);
}, 3000);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={log}>Click me</button>
</div>
);
}
Copy the code
This is what we want it to look like. The page goes from 0, 1, 2, 3, and then the console outputs 3, 3, 3, and then we break down the rendering process.
- Three clicks, total
4
Time to render,count
从0
into3
- Page initialization rendering,
count = 0
.currentCount.current = 0
, page display0
“, rendering completed, triggeruseEffect
.currentCount.current = 0
- The first time I click,
count = 0
, after rendering,count = 1
, page display1
To triggeruseEffect
.currentCount.current = 1
- The second time I hit it,
count = 1
, after rendering,count = 2
, page display2
To triggeruseEffect
.currentCount.current = 2
- On the third click,
count = 2
, after rendering,count = 3
, page display3
To triggeruseEffect
.currentCount.current = 3
- Three clicks done,
currentCount.current = 3
On the fourth render, the page is seencount = 3
.setTimeout
Is called incurrentCount
This object, the output is both3
useEffect
The return value of the function
type EffectCallback = () = > void | (() = > void | undefined);
Copy the code
The useEffect callback can return null or a function. If a function is returned, the function returned by the previous effect callback will be executed first
useEffect(() = > {
console.log('after render');
return () = > {
console.log('last time effect return');
};
});
Copy the code
With this useEffect, the console will output last time effect return and then after render
UseEffect and the class component lifecycle
As mentioned earlier, useEffct takes two arguments. The second argument is an optional list of effects’ dependencies. React decides whether to execute the callback after rendering based on whether or not the values of the list have changed
If you do not pass this parameter, React will assume that the effect is executed after each rendering, which is equivalent to the unconstrained componentDidUpdate lifecycle
useEffect(() = > {
currentCount.current = count;
});
componentDidUpdate() {
currentCount.current = this.state.count;
}
Copy the code
If this parameter is an empty array, React will assume that any state or property changes in the component will not trigger the effect, which means that the effect is only performed once after the component is rendered, and will not trigger any subsequent updates to the component. Equivalent componentDidMount
useEffect(() = >{ currentCount.current = count; } []);componentDidMount() {
currentCount.current = this.state.count;
}
Copy the code
When combined with the return function of the useEffect callback, an effect like componentWillUnmount can be achieved, because if the array is empty, any update to the component will not trigger effect, so the return function of the callback can only be executed when the component is destroyed
useEffect(() = > {
return () = > {
console.log('will trigger ar willUnmount')}}, []);componentWillUnmount() {
console.log('will trigger ar willUnmount')}Copy the code
If there is a value in the dependency list, then something like componentDidMount is conditionally updated, only if the last status is different from this one
useEffect(() = > {
currentCount.current = count;
}, [count]);
componentDidUpdate(prevProps, prevState) {
if(prevState.count ! = =this.state.count) {
currentCount.current = this.state.count; }}Copy the code
useEffect
And closure problems
Suppose that the component needs to define a timer that increments the count at initialization time, it would be natural to write the following code
// Initialize count = 0
useEffect(() = > {
const id = setInterval(() = > {
setCount(count + 1);
}, 1000); } []);componentDidMount() {
setInterval(() = > {
this.setState({ count: this.state.count + 1 });
}, 1000);
}
Copy the code
But in practice, the class component is displayed correctly, and after the function component increments from 0 to 1, the page rendering is never changed
- As I mentioned before, class components because there are
this
This reference, it’s easy to passstate
Get the latest value - Function components are independent closures each time they are rendered, in this case because the write dependency value is
[]
, so only after the first render, this will be doneeffect
, after the first rendering,count
is0
, sosetCount(count + 1)
Every time it’s executionsetCount(0 + 1)
, so the timer works normally, but the value is wrong.
The pointcut and occurrence scenario of the closure problem
Closure problems mostly occur when some callback execution depends on some component state that is not written to the useEffect dependency list. The component state is not up to date when the callback is executed. The main scenes are:
- The timer
- Event listener callback
- A variety of
Observer
The callback
In these scenarios, it is usually only necessary to define a callback function once the component has been initialized and rendered, but if the callback function depends on a component’s transition or property, be careful about closures
function Router() {
const [state, setState] = useState<string> (' ');
useEffect(() = > {
window.addEventListener<'hashchange'> ('hashchange'.() = > {
// Listen for hash changes, depending on state
},
false); } []); }Copy the code
For example, after the component is rendered and listens for hashChange, the callback function does not get any subsequent updates to the state, only the empty string that was initialized.
Try to solve the closure problem – listenstate
change
Since the callback function needs to get the latest state every time, it can listen for changes in state. When the state changes, it can redefine the event listener
function Router() {
const [state, setState] = useState<string> (' ');
useEffect(() = > {
window.addEventListener(
'hashchange'.() = > {
// Listen for hash changes, depending on state
},
false
);
}, [state]);
}
Copy the code
The code above works, but each time state changes, a hashChange callback is redefined, but the last hashChange event listener is not cleared. The code runs, but the memory leak is too bad. You can use the function returned by the useEffect callback to clear the last event listener
function Router() {
const [state, setState] = useState<string> (' ');
useEffect(() = > {
const callback = () = > {};
window.addEventListener('hashchange', callback, false);
return () = > window.removeEventListener('hashchange', callback, false);
}, [state]);
}
Copy the code
So the memory leak problem is solved, but this kind of thing listens, normally it is good to set once, there is no need to redefine, what other better way?
Try to solve the closure problem –setState
Another way to update the state of a component
In addition to passing a value, the function that useState returns to update the state can also pass a callback function. The callback function takes an argument, and the argument is the latest state. In this case, the previous timer example can be modified to look like this.
function Counter() {
const [count, setCount] = useState(0);
useEffect(() = > {
const id = setInterval(() = > {
// setCount(count + 1)
setCount((c) = > c + 1);
}, 1000);
return () = > clearInterval(id); } []);return <h1>{count}</h1>;
}
Copy the code
SetCount ((c) => c + 1)); setCount((c) => c + 1) Return the latest value of count, which is setCount((c) => c + 1), c inside.
Similarly, for event listeners, we can use this method to get the latest state, but there are a few problems
- This callback function, in fact, only needs to get the latest
state
, so in the callsetState
When you get the latest value, remember tosetState
Is set to the same value as the current one. If there is no return, then callsetState
After that,state
The value of theta is going to be thetaundefined
setState
Does returning the same value cause the component and its children to be rerendered? When you call the update function of the State Hook and pass in the current State, React skips the subcomponent rendering and effect execution. Note that React may still need to render this component before skipping the render. Don’t worry, though, since React doesn’t unnecessarily render the “deep” nodes of the component tree- It looks like it’s going to work, but with a few simple changes it could actually work like this
function Router() {
const [state, setState] = useState<string> (' ');
useEffect(() = > {
const callback = () = > {
letLatestState = ' '; setState((stateCallback) = > {
// stateCallback is the latest state
latestState = stateCallback;
// Remember to return stateCallback, otherwise state will be changed to undefined
return stateCallback;
});
// latestState has been assigned to the latestState};window.addEventListener<'hashchange'> ('hashchange', callback, false); }}, [])Copy the code
That should be fine. You can only define a callback once, and then you can get the latest state, but there’s still a problem
-
If the setState callback does not write return stateCallback; This code, which causes state to be set to undefined for no apparent reason, is very hard to find and poorly maintainable
-
SetState is for changing the state of a component, it’s not for you to do that, although it’s perfectly fine to do that. But maintainability is so bad, if your code gets taken over, people are wondering why it’s being written this way, and with no comments and bad variable names, it’s basically zero maintainability, right
-
Setting the same state does not cause the subcomponent to be rerendered, but it is possible for this component to be rerendered, according to the official website
The plan is not perfect. Let’s think outside the box, okay? When executing the callback function, we need to get the latest state. Can we cache state with an unchanging value? Etc?? Constant value??
Best practices for solving closures –useState
anduseRef
The return of useRef is an object that remains constant throughout the lifetime of the component, and you can use useRef to get the latest state. For example, the example could look like this:
function Router() {
const [state, setState] = useState<string> (' ');
const stateRef = useRef<string>(state);
// This allows you to bind stateRef to the latest state
stateRef.current = state;
// Alternatively, you can bind stateRef to the latest state
useEffect(() = > {
stateRef.current = state;
}, [state]);
useEffect(() = > {
const callback = () = > {
const latestState = stateRef.current;
};
window.addEventListener<'hashchange'> ('hashchange', callback, false); } []); }Copy the code
Stateref. current = stateref. current = stateref. current = stateref. current = stateref. current = stateref. current = stateRef.
useRef
和 useState
Best practices for
useState
和useRef
What animal is similar to a class component? Whether andthis.state
和this
Properties of- In the class component, if there is no rendered property, directly hang
this
If you need to participate in rendering properties, hang onthis.state
上 - Similarly, in the
Hook
,useRef
和useState
A similar effect can be achieved
Take the following example
// Function component
const Child = React.memo(() = > {
// Count to participate in page rendering
const [count, setCount] = useState(0);
// userInfo does not render
const userInfo = useRef(null);
});
/ / class components
class Child extends React.PureComponent {
constructor(props) {
super(props);
// Do not render
this.userInfo = null;
// Attributes that participate in rendering
this.state = {
count: 0}; }}Copy the code
Look again atuseEffect
The return value of the callback function
type EffectCallback = () = > (void | (() = > void | undefined));
return void | (() = > void | undefined)
Copy the code
This callback is not an asynchronous function that returns a Promise object by default, so this is not recommended
const [data, setData] = useState({ hits: []}); useEffect(async() = > {const result = await axios(
'url'); setData(result.data); } []);Copy the code
To get around this problem, let’s rewrite it
useEffect(() = > {
const fetchData = async() = > {const result = await axios('url'); setData(result.data); }; fetchData(); } []);Copy the code
useCallback
It’s ok to write a function in it, it’s recommended, but if I have a side effect that I need to handle multiple functions or a long function, one that’s ugly or one that’s too hard to maintain, you can use useCallback to pull the function out. UseCallback returns a memorized function, The dependency list returns a new function if and only if any of its properties change, so this feature is suitable for callbacks passed to child components
function Counter() {
const [count, setCount] = useState(0);
const getFetchUrl = useCallback(() = > {
return 'https://v? query=' + count;
}, [count]);
useEffect(() = > {
getFetchUrl();
}, [getFetchUrl]);
return <h1>{count}</h1>;
}
Copy the code
Here, if the count changes, the value of getFetchUrl changes, causing the useEffect to fire
React.memo
React.memo() returns a memorized value. If React internally determines that if the props’ are not the same as the props’, it will be rerendered. If there are no changes, it will not trigger component rendering. Using this feature can reduce unnecessary subcomponent re-rendering
const Child = memo((props) = > {
useEffect(() = >{}, [])return (
// ...)},(prevProps, nextProps) = > {
// Logic for determining equality
// If some property changes do not require rerendering
// You can write this function
})
Copy the code
React.useCallback
和 React.memo
-
UseCallback returns a cached function in the function component. Each time the component function is executed, the function inside the component is redefined. In this case, The callback function passed by the parent component to the child component changes each time the rendering is performed
-
From the memo point of view, every time the parent component is rendered, the child function will be rerendered without the memo, even if the child has no dependencies and no properties
-
If you add a useCallback to the parent component separately for the child component’s callback function, you can avoid redefining the callback function. However, if the child component is not wrapped in a memo, it will still cause the child component to be rerendered even if any of the child component’s properties have not changed.
-
Similarly, if the child component is wrapped in a memo separately, the parent component will still be redefined every time it renders the callback function
-
Therefore, both Memo and useCallback must be used, otherwise it will be useless, not only fail to achieve the optimization effect, but also increase the burden of React comparison. Either don’t use it, or use it all.
React.useCallback
和 React.memo
Best practices
The parent wraps functions with useCallback, and the child wraps components with Memo, or none at all
/ / child component
// Callback is the callback passed by the parent component
const Child = ({ callback }) = > {}
// The child components are wrapped in React. Memo
export default React.memo(Child);
Const Parent = () => {
// Subcomponent callbacks are wrapped in usecallBacks
const callback = React.useCallback(() = >{} []);return <Child callback={callback} />
};
Copy the code
Raect.memo
The limitations of
-
If the parent component causes the child component to rerender, the memo component will determine whether the property has changed from the last rendering time. If it has changed, the child component will rerender, otherwise it will not rerender.
-
React.memo has a limitation that it can only prevent properties from coming from outside the component. If properties are from inside the component, react.memo has no effect. For example, it cannot prevent properties from being injected directly into the component by useContext
export const Store = React.createContext(null);
export default function Parent() {
const [state, dispatch] = useReducer(reducer, { count: 0.step: 0 });
return (
<Store.Provider value={{ dispatch.state}} >
<Step />
<Count />
</Store.Provider>
);
}
export const Count = memo(() = > {
const { state, dispatch } = useContext(Store);
const setCount = () = > dispatch({ type: 'changeCount' });
return (
<>
<span>count: {state.count}</span>
<button onClick={setCount}>change count</button>
</>
);
});
export const Step = memo(() = > {
const { state, dispatch } = useContext(Store);
const setCount = () = > dispatch({ type: 'changeStep' });
return (
<>
<span>count: {state.step}</span>
<button onClick={setStep}>change step</button>
</>
);
});
Copy the code
- The components above,
count
orstep
Any change in this property will cause the two subcomponents to be rerendered, which is clearly incorrect.
React.useMemo
Instead ofReact.momo
The difference between useMemo and useCallback is that it returns a mnemonized function and a mnemonized value. If useMemo’s callback function is executed and returns a function, It will work just as well as useCallback. Therefore, the above component can be changed. This way of writing prevents any property change from causing two subcomponents to be rerendered
export const Count = () = > {
const { state, dispatch } = useContext(Store);
const setCount = () = > dispatch({ type: 'changeCount' });
return useMemo(
() = > (
<>
<span>count: {state.count}</span>
<button onClick={setCount}>change count</button>
</>
),
[state.count]
);
};
export const Step = () = > {
const { state, dispatch } = useContext(Store);
const setStep = () = > dispatch({ type: 'changeStep' });
return useMemo(
() = > (
<>
<span>step: {state.step}</span>
<button onClick={setStep}>change step</button>
</>
),
[state.step]
);
};
Copy the code
React.momo
和 React.useMemo
React.momo
In terms of preventing subcomponents from being rerendered, there is one in the class componentReact.PureComponent
, its function is also. But it does not detect state changes inside the function and prevents re-rendering, for exampleuseContext
The state of the injection. However, it automatically compares all properties and uses aspects.React.memo
Depending on whether the dependency list has changed properties, decide whether to return the new value, in some degree andVue
Is similar to the computed properties of, but requires that the declared dependent properties be invoked. Compared with theReact.momo
The granularity of its control is more granular, but the general external attribute changes that use this one are obviously notReact.memo
convenient
useReducer
useContext
useReducer
是useState
As an alternative to,useState
The internal implementation ofuseReducer
- It takes two parameters, alpha and beta
redux
Same thing, one isreducer
One is the initial value, and two are returned, always the currentstate
, one isdispatch
- through
dispatch
callaction
I can change itstate
The data inside - Essentially, it decouples the data from the function components so that the function components just emit
Action
, you can modify the data, since the data is not inside the component and does not have to deal with the internalstate
Change brought abouteffect
. useContext
和useReducer
Combination, to a certain extent can achieve aReact Redux
.Nuggets article linksThis is the previous article, I won’t expand it here
otherHook
-
UseImperativeHandle, combined with useRef and forwardRefs, allows a custom parent component to refer to properties and methods of the child component instead of referring to the entire instance of the child component. But you don’t want all the properties and methods to be invoked by the parent component
-
UseLayoutEffect is not used much, and its function is the same as useEffect. However, this hook is called after component changes, after DOM node generation, and before rendering. Different from useEffect, which is called after rendering, it is not recommended to use, because it will block rendering
-
UseDebugValue can be used to display a label for custom hooks in the React developer tools. Similar to Vue’s name or React’s displayName, it does not affect code running
Component reuse
React hooks have custom hooks, React class components have higher-order components or render attributes. There is a common scenario that the back-end interface needs to be called to enter the page. If each component is written once, it will be tedious, assuming that the interface for processing data is like this
interface Response<T> {
/** Whether there is an error */
hasError: boolean;
/** Whether there is data */
hasData: boolean;
/** Whether the request is in */
Loading: boolean;
/** Request back data */
data: T;
}
Copy the code
High order component
High Order Component (HOC) is an advanced technique used in React to reuse component logic. Instead of being part of the React API, HOC is a design pattern based on the React combinatorial features. Reusing components like Connect in react-Redux or withRouter in React Router is common
It can do:
- Property brokers, such as common properties used by multiple components, inject properties
- Wrap a component, such as wrapping it in a written container
- Render hijacking, such as permission control
use
- Code reuse
- Performance monitoring
- Permission control, according to do not understand the permission level, rendering different pages
High-level component writing and use
As soon as a component is rendered according to the requirements requested above, start requesting the initial data
function requestWrapper(options) {
return (Component) = > {
return class WapperComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false.hasError: false.hasData: false.data: null}; } httpRequest =async() = > {this.setState({ loading: true });
// Do some request work here, I will not write here, it is not necessary
this.setState({ hasData: true });
this.setState({ hasError: false });
this.setState({
data: {
/** Here is the data */}});this.setState({ loading: false });
};
componentDidMount() {
this.httpRequest();
}
render() {
// Pass through the attributes passed from the outside, then merge this.state and pass it to the package component
constcombineProps = { ... this.props, ... this.state };return this.state.loading ? (
<Loading />
) : (
<Component {. combineProps} / >); }}; }; }Copy the code
Using aspects, higher-order components can decorate class components or function components
function Page(props) {
// props includes loading hasError hasData data prop1
Prop1 comes from the external attribute and the other attributes come from the component of the package
}
// Class component use, use decorators
@requestWrapper({url: ' '.param: {}})class ClassComponent extends React.PureComponent {}// class component. Decorators are not applicable
export default requestWrapper({url: ' '.param: {} })(ClassComponent);
// Use a functional component
const WrapPage = requestWrapper({ url: ' '.param: {} })(Page);
export default WrapPage;
/ / use
<WrapPage prop1="1" />;
Copy the code
The customHook
Write and use
Custom Hook, a simple data request Hook
function useRequest(options) {
const [loading, setLoading] = useState(false);
const [hasError, setHasError] = useState(false);
const [hasData, setHasData] = useState(false);
const [data, setData] = useState(null);
useEffect(() = > {
async function reqeust() {
setLoading(true);
/** this is a request
setHasError(false);
setHasData(true);
setData({
/** Request back data */
});
setLoading(false); } reqeust(); } []);return { loading, hasError, hasData, data };
}
Copy the code
Custom hooks can only be used in functional components, not in class components
function Page(props) {
// Props for this time is prop1 only
const { loading, hasError, hasData, data } = useRequest({
url: ' ',param: {},}); } <Page prop1={1} / >;Copy the code
Default properties for functional components and class components
class Button extends React.PureComponent {
static defaultProps = { type: "primary".onChange: () = >{}}; }// This can be used for both functions and classes. This is another way to write static properties of a class
Button.defaultProps = {
type: "primary".onChange: () = >{}};function Button({ type, onChange }) {}
// This looks fine, but in fact, if the parent component does not pass onChange, onChange
// Each time the component is rendered, a new function is generated. Reference types have this problem
function Button({ type = "primary", onChange = () => {} }) {}
// That's OK. You are really a dodger
const changeMet = () = > {}
function Button({type = "primary", onChange = changeMet}) {}
Copy the code
Has the class component problem been resolved?
It is difficult to reuse component state logic
- Relying on custom hooks solves the problem of component state and logic reuse, but custom
Hook
Writing needs to matchHook
The operating mechanism is well understood and the threshold is no lower than for higher-order components
Life cycle brings negative effects and logic splits seriously
- Lifecycle split logic issues in
Hook
Inside is actually solved, there is no same logic to be split inN
In the entire life cycle
This points to the problem
- This problem is also solved in the Hook, because the function does not
this
, there won’t bethis
If you need an immutable object, useuseRef
Simple summary
-
UseState can implement effects similar to state and setState
-
UseEffect componentDidMount componentDidUpdate componentWillUnmount this life cycle function, and write more simple, after each rendering will trigger, trigger the condition is that the dependency has changed
-
UseRef returns a reference that returns the same object each time it is rendered, consistent with the this property of the class component
-
UseCallback returns a memorized callback function. If a dependency changes, the callback function is changed. Otherwise, the previous callback function is returned
-
UseMemo returns a mnemonized value, which changes only when dependencies change. It can be used to mnemonize values, similar to Vue properties, to avoid double computation and to avoid repeated rendering
-
Custom hooks enable state and logic reuse, much like higher-order components and render properties
-
UseReducer is the underlying implementation of useState that manages multiple states and pulls them out of the components
-
UseContext can be used to batch pass values, inject multiple components, and useReducer useMemo can use the function of Redux
Use feeling
Personal use
-
Functional components themselves write a lot less code than class components
-
Closure issues have a significant impact on development and debugging, raising debugging costs and making it difficult to detect problems if you are not familiar with closure mechanisms. Most of the closure problems in hooks are due to unfilled dependencies
-
Closures are more annoying than the This component, and major debugging is not easy to find, and filling in dependencies can be a tricky chore
-
The dependency of Hook cannot be identified automatically and must be declared manually. Although the plug-in is added, it is still not as good as Vue’s Hook
-
When you are familiar with the mechanism of hooks, the development experience of hooks is much better than that of class components
Team work
- It’s actually promoting
Hook
When the team membersHook
As mentioned above, there are various ways to solve the closure problem. In fact, I found out by myself. Then I saw that the team members were almost still usersstate
After the update, we reset the way we listen, which is not very good, but the closure problem is resolved - In contrast,
React
The official also did not summarize many best practices, many rely on their own practices, so the team members are just in contactHook
“, bothuseEffect
useState
twoAPI
And even inReact Hook
In the official documentHook profileFor both of theseHook
A lot of introductions - But for other commonly used
Hook
, such asuseRef
和useCallback
Usage scenarios don’t really have good examples to support theseAPI
The use of. In fact, many members of the team do not participate in the rendering of the property, but also useuseState
Instead of usinguseRef
. It’s a lot of new contactHook
An easy mistake to make. - There are many students some plug-ins did not install, resulting in
React
A plug-in that automatically detects dependencies does not work, which frosts a closure problem that is already hard to find - So I also regularly share what I think is a good practice in the team to guide the students in the team
- For people who don’t like to use
React Hook
Students, directly use class components, class components although the code is cumbersome to write, but at least there is no closure problems, and the code is easy to read after taking over,React Hook
It’s just a tool, and if you use it, you’ll get points, but if you don’t use it, you’ll only use class components, and it won’t affect other people’s code. Compare class components and function components can coexist