Contents
- 1 What is React Hooks?
- 2 READ ALSO
- 3 Configuring VS Code for Node/JavaScript Development
- 4 How does Nodejs solve the problem of high concurrency?
- 5 React state sharing scheme
- 6 React Hooks design concept
- 7 Fundamental
- 8 Functional implementation
- 9 Come to a set of combination punches!
- 10 useState maintains component state
- 11 useWillUnmount
- 12 As mentioned in the useEffect, it allows to return a cleanup function, the component will execute the cleanup function when unmounting, so useWillUnmount can also be easily implemented~
- 13 Precautions
- 14 Conclusion
What is React Hooks?
Hooks, as the name implies, is literally the concept of a React hook. Through a case, we have the first impression of React Hooks.
Suppose now that you want to implement a component of a counter. If we use componentization, we need to do a lot more things, such as declaring state, writing counters, etc., and there are more concepts to understand, such as the concept of Javascript classes, the context of this context, etc. .
Example
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class Counter extends React.Component { state = { count: 0 } countUp = () => { const { count } = this.state; this.setState({ count: count + 1 }); } countDown = () => { const { count } = this.state; this.setState({ count: count - 1 }); } render() { const { count } = this.state; return ( <div> <button onClick={this.countUp}>+</button> <h1>{count}</h1> <button onClick={this.countDown}>-</button> </div> ) } } ReactDOM.render(<Counter />, document.getElementById('root'));
With React Hooks, we can write this.
Example
import React, { useState } from 'react'; import ReactDOM from 'react-dom'; function Counter() { const [count, setCount] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>+</button> <h1>{count}</h1> <button onClick={() => setCount(count - 1)}>-</button> </div> ) } ReactDOM.render(<Counter />, document.getElementById('root'));
Through the above example, it is obvious that React Hooks provides a compact, functional (FP) program style that implements state-to-UI interaction (MVVM) through pure function components and controllable data streams.
Hooks API
- Basic Hooks
- useState
- useEffect
- useContext
- Additional Hooks
- useReducer
- useCallback
- useMemo
- useRef
- useImperativeHandle
- useLayoutEffect
- useDebugValue
useState
useState is the most basic API, it passes in an initial value, and each function can get a new value.
import React, { useState } from 'react'; import ReactDOM from 'react-dom'; function Counter() { const [count, setCount] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>+</button> <h1>{count}</h1> <button onClick={() => setCount(count - 1)}>-</button> </div> ) } ReactDOM.render(<Counter />, document.getElementById('root'));
It should be noted that the state count obtained by useState is represented as a constant in the Counter component. Each time it is modified by setCount, it re-acquires a new constant through useState.
useReducer
useReducer and useState are almost identical, requiring an external reducer (global), in this way multiple states can be controlled simultaneously. Looking closely, it is very close to the concept of data flow in redux.
import { useState, useReducer } from 'react'; import ReactDOM from 'react-dom'; function reducer(state, action) { switch (action.type) { case 'up': return { count: state.count + 1 }; case 'down': return { count: state.count - 1 }; } } function Counter() { const [state, dispatch] = useReducer(reducer, { count: 1 }) return ( <div> {state.count} <button onClick={() => dispatch({ type: 'up' })}>+</button> <button onClick={() => dispatch({ type: 'down' })}>+</button> </div> ); } ReactDOM.render(<Counter />, document.getElementById('root'));
useEffect
A vital Hooks API, as its name implies, useEffect is used to handle the side effects of various state changes, that is, the logic that will only be executed at a particular moment.
import { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; return function cleanup() { document.title = 'app'; } }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ReactDOM.render(<Example />, document.getElementById('root'));
useMemo
useMemo is mainly used for rendering process optimization. The two parameters are the calculation function (usually the component function) and the dependency state list. When the state of the dependency changes, the execution of the calculation function is triggered. If no dependencies are specified, the calculation function is executed every rendering process.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
import { useState, useMemo } from 'react'; import ReactDOM from 'react-dom'; function Time() { return <p>{Date.now()}</p>; } function Counter() { const [count, setCount] = useState(0); const memoizedChildComponent = useMemo((count) => { return <Time />; }, [count]); return ( <div> <h1>{count}</h1> <button onClick={() => setCount(count + 1)}>+</button> <div>{memoizedChildComponent}</div> </div> ); } ReactDOM.render(<Counter />, document.getElementById('root'));
useContext
Context is the state of external create, internal use, and the difference between it and global variables is that if multiple components use context at the same time, then these components will be re-render. If multiple components useState with the same global variable, only the current component that triggers setState will be triggered. Rerender.
Example – unused useContext
import { useState, useContext, createContext } from 'react'; import ReactDOM from 'react-dom'; const UserContext = new createContext(); const UserProvider = props => { let [username, handleChangeUsername] = useState(''); return ( <UserContext.Provider value={{ username, handleChangeUsername }}> {props.children} </UserContext.Provider> ); }; // 3. Create Consumer const UserConsumer = UserContext.Consumer; // 4. Use Consumer Package component const Pannel = () => ( <UserConsumer> {({ username, handleChangeUsername }) => ( <div> <div>user: {username}</div> <input onChange={e => handleChangeUsername(e.target.value)} /> </div> )} </UserConsumer> ); const Form = () => <Pannel />; const App = () => ( <div> <UserProvider> <Form /> </UserProvider> </div> ); ReactDOM.render(<App />, document.getElementById('root'));
Example – Using the useContext
import { useState, useContext, createContext } from 'react'; import ReactDOM from 'react-dom'; const UserContext = new createContext(); const UserProvider = props => { let [username, handleChangeUsername] = useState(''); return ( <UserContext.Provider value={{ username, handleChangeUsername }}> {props.children} </UserContext.Provider> ); }; const Pannel = () => { const { username, handleChangeUsername } = useContext(UserContext); // 3. 使用 Context return ( <div> <div>user: {username}</div> <input onChange={e => handleChangeUsername(e.target.value)} /> </div> ); }; const Form = () => <Pannel />; const App = () => ( <div> <UserProvider> <Form /> </UserProvider> </div> ); ReactDOM.render(<App />, document.getElementById('root'));
useRef
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will last the entire component’s life cycle. In fact, useRef is a very useful API. In many cases, we need to save some changes, it will come in handy.
Example
function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
React state sharing scheme
When it comes to state sharing, the easiest and most straightforward way is to pass state through props. This method is coupled with the parent-child relationship of the component. Once the component nesting structure changes, the code needs to be rewritten, and the maintenance cost is very expensive. Over time, various programs have been officially introduced to address the issue of state sharing and code reuse.
Mixins
In React, only components created with createClass can use mixins. This high coupling relies on uncontrollable, high-complexity approaches that have faded out of the historical arena with the wave of ES6.
HOC
Higher-order components are derived from functional programming. Since components in React can also be treated as functions (classes), code reuse can be achieved by HOC. It can be implemented by attribute proxy and reverse inheritance. HOC can easily manipulate the result of rendering, and can also operate on the props/state of the component, so that complex code logic multiplexing can be conveniently performed.
import React from 'react'; import PropTypes from 'prop-types'; class Show extends React.Component { static propTypes = { children: PropTypes.element, visible: PropTypes.bool, }; render() { const { visible, children } = this.props; return visible ? children : null; } } function Show2(WrappedComponent) { return class extends WrappedComponent { render() { if (this.props.visible === false) { return null; } else { return super.render(); } } } } function App() { return ( <Show visible={Math.random() > 0.5}>hello</Show> ); }
State multiplexing in Redux is a typical implementation of HOC. We can assemble data into target components through compose, but you can also handle it through decorators.
import React from 'react'; import { connect } from 'react-redux'; @connect(state => ({ name: state.user.name })) class App extends React.Component{ render() { return <div>hello, {this.props.name}</div> } } connect((state) => ({ name: state.user.name }))(App);
Render Props
Obviously, renderProps is a way to pass the render method as a prop to a child component. Compared to the HOC solution, renderProps can protect the original component hierarchy.
import React from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; class Mouse extends React.Component { static propTypes = { render: PropTypes.func.isRequired } state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ); } } function App() { return ( <div style={{ height: '100%' }}> <Mouse render={({ x, y }) => ( // render prop 给了我们所需要的 state 来渲染我们想要的 <h1>The mouse position is ({x}, {y})</h1> )}/> </div> ); } ReactDOM.render(<App/>, document.getElementById('root'));
Hooks
By combining the Hooks API with React's built-in Context, you can see from the previous example that Hook makes state sharing between components clearer and simpler.
React Hooks design concept
Fundamental
function FunctionalComponent () { const [state1, setState1] = useState(1); const [state2, setState2] = useState(2); const [state3, setState3] = useState(3); }
{ memoizedState: 'foo', next: { memoizedState: 'bar', next: { memoizedState: 'bar', next: null } } }
Functional implementation
Capture props
Function components are inherently supported for props, and the basic usage is not much different from the class components. The two differences that need to be noted are:
- The class component props are mounted in this context, and the functional component is passed in by the formal parameter;
- Due to the difference in mount position, if this changes in the class component, this.props will also change; in the function component props is always immutable, so follow the capture value principle (that is, the value obtained is always At some point in time, Hooks also follows this principle.
To understand the capture value with an example, we can use the useRef to evade the capture value because the useRef is mutable.stateClass component
Function component
Creation status | this.state = {} | |
---|---|---|
useState, useReducer | Modify status | this.setState() |
set function | Update mechanism | Asynchronous update, multiple modifications merged to the previous state, resulting in a copy |
Synchronous update, directly modified to target status | State management | One state centrally manages multiple states |
Multiple states, state merge can be done via useReducer (manual) | performance | high |
If the useState initialization state needs to be obtained through very complicated calculations, please use the declaration method of the function, otherwise, it will be executed repeatedly for each rendering. | The life cycle | componentDidMount / componentDidUpdate / componentWillUnMount |
useEffect is called every time it is rendered, and it can be used as a lifecycle with a little wrapper;
- shouldComponentUpdate
Usually, when we optimize component performance, we prefer to use pure components to reduce the number of renderings of individual components.
- Reuse Hooks can be replaced by useMemo, which can re-render components only when some data changes, equivalent to shouldComponentUpdate with shallowEqual.
Force rendering forceUpdate
class Button extends React.PureComponent {}
Since by default, each modification state will cause re-rendering, it can be used as a forceUpdate through a set function that is not used.
Principle of implementation
Enhance Hooks based on Hooks
const forceUpdate = () => useState(0)[1];
Come to a set of combination punches!
Since each Hooks API is a pure function concept, it pays more attention to input and output. Therefore, it is better to assemble the function and combine the basic Hooks APIs of different features to create a new one. Hooks of features.
useState maintains component state
useEffect handles side effects
- useContext listens for provider update changes
- useDidMount
- useDidUpdate
useWillUnmount
import { useEffect } from 'react'; const useDidMount = fn => useEffect(() => fn && fn(), []); export default useDidMount;
As mentioned in the useEffect, it allows to return a cleanup function, the component will execute the cleanup function when unmounting, so useWillUnmount can also be easily implemented~
import { useEffect, useRef } from 'react'; const useDidUpdate = (fn, conditions) => { const didMoutRef = useRef(false); useEffect(() => { if (!didMoutRef.current) { didMoutRef.current = true; return; } return fn && fn(); }, conditions); }; export default useDidUpdate
useHover
Example
import { useEffect } from 'react'; const useWillUnmount = fn => useEffect(() => () => fn && fn(), []); export default useWillUnmount;
useField
Example
import { useState } from 'react'; const useHover = () => { const [hovered, set] = useState(false); return { hovered, bind: { onMouseEnter: () => set(true), onMouseLeave: () => set(false), }, }; }; export default useHover;
import { useHover } from './lib/onHover.js'; function Hover() { const { hovered, bind } = useHover(); return ( <div> <div {...bind}> hovered: {String(hovered)} </div> </div> ); }
Precautions
The scope of use of Hook: in the functional React component, in the custom Hook function;
import { useState } from 'react'; const useField = (initial) => { const [value, set] = useState(initial); return { value, set, reset: () => set(initial), bind: { value, onChange: e => set(e.target.value), }, }; } export default useField;
import { useField } from 'lib/useField'; function Input { const { value, bind } = useField('Type Here...'); return ( <div> input text: {value} <input type="text" {...bind} /> </div> ); } function Select() { const { value, bind } = useField('apple') return ( <div> selected: {value} <select {...bind}> <option value="apple">apple</option> <option value="orange">orange</option> </select> </div> ); }
Precautions
- The scope of use of Hook: in the functional React component, in the custom Hook function;
- Hook must be written at the outermost layer of the function. Each time useState changes its cursor, and React updates the state according to its order.
- Although the Hook API is executed every time the render is executed, the resulting state is always a constant (the scope is inside the function);
Conclusion
React Hooks provides new possibilities for state management, although we may need to maintain some internal state, but avoid dealing with state management issues in complex ways such as renderProps / HOC. The benefits of Hooks are as follows:
- More granular code reuse without excessive side effects
- Functional programming style, code is more concise while reducing the threshold for use and understanding
- Reduce component nesting levels
- Component data flow is clearer
In fact, by customizing custom Hooks in various scenarios, our application is more convenient and concise, the hierarchy of components is guaranteed to be intact, and there is such a pleasing functional programming style, Hooks in React 16.8 The .0 version has been officially released for a stable version, so let’s get started! ! !
References:
https://reactjs.org/docs/hooks-reference.html
https://github.com/beizhedenglong/react-hooks-lib
https://overreacted.io/how-are-function-components-different-from-classes/
Discussion about this post