Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

React delves into everything from Mixin to HOC to Hook

2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "React in-depth analysis from Mixin to HOC and then to Hook". In daily operation, I believe many people have doubts in React in-depth analysis from Mixin to HOC and then to Hook. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts of "React in-depth analysis from Mixin to HOC and then to Hook". Next, please follow the editor to study!

Mixin design pattern

Mixin (blending) is a way to extend the collection function, which essentially copies the properties of one object to another, but you can copy any methods of any number of objects to a new object, which cannot be achieved by inheritance. Its main purpose is to solve the problem of code reuse.

Many open source libraries provide implementations of Mixin, such as the _ .extend method of Underscore and the extend method of JQuery.

Use the _ .extend method to achieve code reuse:

Var LogMixin = {actionLog: function () {console.log ('action...');}, requestLog: function () {console.log (' request...');},}; function User () {/ *. * /} function Goods () {/ *. * /} _ .extend (User.prototype, LogMixin); _ .extend (Goods.prototype, LogMixin); var user = new User () Var good = new Goods (); user.actionLog (); good.requestLog ()

We can try to write a simple Mixin method manually:

Function setMixin (target, mixin) {if (arguments [2]) {for (var I = 2, len = arguments.length; I)

< len; i++) { target.prototype[arguments[i]] = mixin.prototype[arguments[i]]; } } else { for (var methodName in mixin.prototype) { if (!Object.hasOwnProperty(target.prototype, methodName)) { target.prototype[methodName] = mixin.prototype[methodName]; } } } } setMixin(User,LogMixin,'actionLog'); setMixin(Goods,LogMixin,'requestLog'); 您可以使用setMixin方法将任意对象的任意方法扩展到目标对象上。 React中应用Mixin React也提供了Mixin的实现,如果完全不同的组件有相似的功能,我们可以引入来实现代码复用,当然只有在使用createClass来创建React组件时才可以使用,因为在React组件的es6写法中它已经被废弃掉了。 例如下面的例子,很多组件或页面都需要记录用户行为,性能指标等。如果我们在每个组件都引入写日志的逻辑,会产生大量重复代码,通过Mixin我们可以解决这一问题: var LogMixin = { log: function() { console.log('log'); }, componentDidMount: function() { console.log('in'); }, componentWillUnmount: function() { console.log('out'); } }; var User = React.createClass({ mixins: [LogMixin], render: function() { return (...) } }); var Goods = React.createClass({ mixins: [LogMixin], render: function() { return (...) } }); Mixin带来的危害 React官方文档在Mixins Considered Harmful一文中提到了Mixin带来了危害: Mixin 可能会相互依赖,相互耦合,不利于代码维护 不同的 Mixin 中的方法可能会相互冲突 Mixin非常多时,组件是可以感知到的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性 React现在已经不再推荐使用Mixin来解决代码复用问题,因为Mixin带来的危害比他产生的价值还要巨大,并且React全面推荐使用高阶组件来替代它。另外,高阶组件还能实现更多其他更强大的功能,在学习高阶组件之前,我们先来看一个设计模式。 装饰模式 装饰者(decorator)模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。 高阶组件(HOC) 高阶组件可以看作React对装饰模式的一种实现,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。 高阶组件(HOC)是React中的高级技术,用来重用组件逻辑。但高阶组件本身并不是React API。它只是一种模式,这种模式是由React自身的组合性质必然产生的。 function visible(WrappedComponent) { return class extends Component { render() { const { visible, ...props } = this.props; if (visible === false) return null; return ; } } } 上面的代码就是一个HOC的简单应用,函数接收一个组件作为参数,并返回一个新组件,新组建可以接收一个visible props,根据visible的值来判断是否渲染Visible。 下面我们从以下几方面来具体探索HOC。 HOC的实现方式 属性代理 函数返回一个我们自己定义的组件,然后在render中返回要包裹的组件,这样我们就可以代理所有传入的props,并且决定如何渲染,实际上 ,这种方式生成的高阶组件就是原组件的父组件,上面的函数visible就是一个HOC属性代理的实现方式。 function proxyHOC(WrappedComponent) { return class extends Component { render() { return ; } } } 对比原生组件增强的项: 可操作所有传入的props 可操作组件的生命周期 可操作组件的static方法 获取refs 反向继承 返回一个组件,继承原组件,在render中调用原组件的render。由于继承了原组件,能通过this访问到原组件的生命周期、props、state、render等,相比属性代理它能操作更多的属性。 function inheritHOC(WrappedComponent) { return class extends WrappedComponent { render() { return super.render(); } } } 对比原生组件增强的项: 可操作所有传入的props 可操作组件的生命周期 可操作组件的static方法 获取refs 可操作state 可以渲染劫持 HOC可以实现什么功能 组合渲染 可使用任何其他组件和原组件进行组合渲染,达到样式、布局复用等效果。 通过属性代理实现: function stylHOC(WrappedComponent) { return class extends Component { render() { return ( {this.props.title} ); } } } 通过反向继承实现: function styleHOC(WrappedComponent) { return class extends WrappedComponent { render() { return {this.props.title} {super.render()} } } } 条件渲染 根据特定的属性决定原组件是否渲染。 通过属性代理实现: function visibleHOC(WrappedComponent) { return class extends Component { render() { if (this.props.visible === false) return null; return ; } } } 通过反向继承实现: function visibleHOC(WrappedComponent) { return class extends WrappedComponent { render() { if (this.props.visible === false) { return null } else { return super.render() } } } } 操作props 可以对传入组件的props进行增加、修改、删除或者根据特定的props进行特殊的操作。 通过属性代理实现: function proxyHOC(WrappedComponent) { return class extends Component { render() { const newProps = { ...this.props, user: 'ConardLi' } return ; } } } 获取refs 高阶组件中可获取原组件的ref,通过ref获取组件实力,如下面的代码,当程序初始化完成后调用原组件的log方法。(不知道refs怎么用,请?Refs & DOM) 通过属性代理实现: function refHOC(WrappedComponent) { return class extends Component { componentDidMount() { this.wapperRef.log() } render() { return { this.wapperRef = ref }} />

;}

Note here: when you call a high-level component, you can't get the real ref of the original component. You need to pass it manually. For more information, please see passing refs.

State management

Extract the state of the original component to HOC for management, such as the following code, we extract the value of Input to HOC for management, making it a controlled component without affecting it to use the onChange method to do some other operations. Based on this approach, we can implement a simple two-way binding, see two-way binding for details.

Through property proxies:

Function proxyHoc (WrappedComponent) {return class extends Component {constructor (props) {super (props); this.state = {value:''};} onChange = (event) = > {const {onChange} = this.props This.setState ({value: event.target.value,}, () = > {if (typeof onChange = 'function') {onChange (event);}})} render () {const newProps = {value: this.state.value, onChange: this.onChange,} return } class HOC extends Component {render () {return}} export default proxyHoc (HOC)

Manipulate state

The above example makes some enhancements to the original component through the property broker using the state of HOC, but can not directly control the state of the original component, but through reverse inheritance, we can directly manipulate the state of the original component. However, it is not recommended to directly modify or add the state of the original component, as this may conflict with the operation within the component.

Through reverse inheritance:

Function debugHOC (WrappedComponent) {return class extends WrappedComponent {render () {console.log ('props', this.props); console.log (' state', this.state); return ({super.render ()})}

The above HOC prints out props and state in render and can be used as a debug phase, but of course you can write more debugging code in it. Imagine that you only need to add @ debug to the component we want to debug without having to write a lot of redundant code each time you debug it. If you don't know how to use HOC, please? How to use HOC)

Render hijacking

Higher-order components can do a lot of things in the render function to control the rendering output of the original component. As long as the rendering of the original component is changed, we all call it a rendering hijacking.

In fact, the above combination rendering and conditional rendering are a kind of rendering hijacking. Through reverse inheritance, you can not only achieve the above two points, but also directly enhance the React elements generated by the original component render function.

Through reverse inheritance:

Function hijackHOC (WrappedComponent) {return class extends WrappedComponent {render () {const tree = super.render (); let newProps = {}; if (tree & & tree.type = 'input') {newProps = {value:' rendering hijacked'};} const props = Object.assign ({}, tree.props, newProps) Const newTree = React.cloneElement (tree, props, tree.props.children); return newTree;}}

Note that I am using enhancements instead of changes in the instructions above. Inside the render function is actually the React element generated by calling React.creatElement:

Although we can get it, we can't directly modify the properties in it. We print its configuration items through the getOwnPropertyDescriptors function:

You can see that all writable properties are configured as false, that is, all properties are immutable. (if you have any questions about these configuration items, please defineProperty)

Instead of modifying it directly, we can use the cloneElement method to enhance a new component based on the original component:

React.cloneElement () clones and returns a new React element, using element as the starting point. The resulting element will have a shallow merge of the original element props and the new props. The new child replaces the existing child. Key and ref from the original element will be retained.

React.cloneElement () is almost equivalent to:

{children}

How to use HOC

The above sample code is all about how to declare that a HOC,HOC is actually a function, so we will enhance the component as a parameter to call the HOC function, the enhanced component.

Class myComponent extends Component {render () {return (original component)}} export default inheritHOC (myComponent)

Compose

In practical applications, a component may be enhanced by multiple HOC, and we are using components enhanced by all HOC. It may be easier to understand by using a decorative pattern diagram to illustrate:

Suppose we now have multiple HOC, such as logger,visible,style, and now we want to enhance an Input component at the same time:

Logger (visible (style (Input)

This kind of code is very difficult to read. We can manually encapsulate a simple function combination tool and rewrite it as follows:

Const compose = (... fns) = > fns.reduce ((f, g) = > (... args) = > g (f (.. ARGs); compose (logger,visible,style) (Input)

The compose function returns a function with a combination of all functions, compose (f, g, h) and (... args) = > f (g (h (.. ARGs) are the same.

Many third-party libraries provide functions similar to compose, such as the combineReducers function provided by lodash.flowRight,Redux.

Decorators

We can also use the Decorators provided by ES7 to make our writing more elegant:

Logger @ visible @ style class Input extends Component {/ /...}

Decorators is a proposal of ES7 and has not been standardized yet, but the Babel transcoder is already supported, so we need to configure babel-plugin-transform-decorators-legacy in advance:

"plugins": ["transform-decorators-legacy"]

You can also use it in conjunction with the compose function above:

Const hoc = compose (logger, visible, style); @ hoc class Input extends Component {/...}

Practical Application of HOC

The following are some practical application scenarios of HOC in the production environment. Due to the length of the article, the code has been simplified. If you have any questions, please point them out in the comments section:

Log management

In fact, this is one of the most common applications, multiple components have similar logic, we need to reuse the repeated logic, the example of CommentList in the official document also solves the problem of code reuse, it is written in great detail, can you be interested? Use high-level components (HOC) to address crosscutting concerns.

Some pages need to record user behavior, performance metrics, and so on, and doing these things through high-level components can save a lot of repetitive code.

Function logHoc (WrappedComponent) {return class extends Component {componentWillMount () {this.start = Date.now ();} componentDidMount () {this.end = Date.now (); console.log (`${WrappedComponent.dispalyName} render time: ${this.end-this.start} ms`); console.log (` ${user} enter ${WrappedComponent.dispalyName} `) } componentWillUnmount () {console.log (`${user} exit ${WrappedComponent.dispalyName}`);} render () {return}

Available, permission control

Function auth (WrappedComponent) {return class extends Component {render () {const {visible, auth, display = null,... props} = this.props; if (visible = false | | (auth & & authList.indexOf (auth) = =-1)) {return display} return;}

AuthList is the list of all permissions that we request from the backend when we enter the program. When the permissions required by the component are not in the list, or when the visible set is false, we display it as the passed component style, or null. We can apply HOC to any component that requires permission verification:

@ auth class Input extends Component {...} @ auth class Button extends Component {...} add user add user

Two-way binding

In vue, two-way data binding can be achieved after binding a variable, that is, the bound variable will change automatically when the value in the form changes. This is not done in React, and by default, form elements are uncontrolled components. After binding a state to a form element, you often need to manually write the onChange method to rewrite it into a controlled component, and these repetitive operations are painful in the case of a large number of form elements.

We can use high-level components to achieve a simple two-way binding, the code is slightly longer, can be combined with the following mind map to understand.

First, we customize a Form component, which is used to wrap all the form components that need to be wrapped, exposing two properties to the child components through contex:

Model: all data currently controlled by Form, consisting of forms name and value, such as {name:'ConardLi',pwd:'123'}. Model can be imported from outside, or it can be controlled by itself.

ChangeModel: change the value of a name in model.

Class Form extends Component {static childContextTypes = {model: PropTypes.object, changeModel: PropTypes.func} constructor (props, context) {super (props, context); this.state = {model: props.model | | {}} } componentWillReceiveProps (nextProps) {if (nextProps.model) {this.setState ({model: nextProps.model})} changeModel = (name, value) = > {this.setState ({model: {... this.state.model, [name]: value}})} getChildContext () {return {changeModel: this.changeModel Model: this.props.model | | this.state.model} } onSubmit = () = > {console.log (this.state.model);} render () {return {this.props.children} submit}}

The following defines the HOC for bidirectional binding, which proxies the onChange property and the value property of the form:

When the onChange event occurs, the changeModel method of the upper Form is called to change the model in the context.

Change value to the value taken out of context at render time.

Function proxyHoc (WrappedComponent) {return class extends Component {static contextTypes = {model: PropTypes.object, changeModel: PropTypes.func} onChange = (event) = > {const {changeModel} = this.context; const {onChange} = this.props; const {v_model} = this.props; changeModel (v_model, event.target.value); if (typeof onChange = 'function') {onChange (event) }} render () {const {model} = this.context; const {v_model} = this.props; return;}} @ proxyHoc class Input extends Component {render () {return}}

The above code is only a brief part. In addition to input, we can also apply HOC to other form components such as select, and even make the above HOC compatible with span, table and other presentation components. This greatly simplifies the code and saves us a lot of state management work. Use the following:

Export default class extends Component {render () {return ()}}

Form check

Based on the above example of two-way binding, let's take another form validator, which can contain validation functions and prompt messages, and display error messages when validation fails:

Function validateHoc (WrappedComponent) {return class extends Component {constructor (props) {super (props); this.state = {error:''}} onChange = (event) = > {const {validator} = this.props If (validator & & typeof validator.func = = 'function') {if (! validator.func (event.target.value)) {this.setState ({error: validator.msg})} else {this.setState ({error:'})} render () {return {this.state.error | |'} const validatorName = {func: (val) = > val & &! isNaN (val) Msg: 'please enter a number'} const validatorPwd = {func: (val) = > val & & val.length > 6, msg: 'password must be greater than 6 digits'}

Of course, you can also determine whether all validators pass when Form is submitted, validators can also be set to an array, and so on. Due to the length of the article, the code has been simplified a lot, and interested students can implement it on their own.

Connect of Redux

Connect in redux is actually a HOC. Here is a simplified version of connect implementation:

Export const connect = (mapStateToProps MapDispatchToProps) = > (WrappedComponent) = > {class Connect extends Component {static contextTypes = {store: PropTypes.object} constructor () {super () this.state = {allProps: {} componentWillMount () {const {store} = this.context this._updateProps () store.subscribe (() = > this. _ updateProps ()} _ updateProps () {const {store} = this.context let stateProps = mapStateToProps? MapStateToProps (store.getState (), this.props): {} let dispatchProps = mapDispatchToProps? MapDispatchToProps (store.dispatch, this.props): {} this.setState ({allProps: {... stateProps,... dispatchProps,... this.props}})} render () {return}} return Connect}

The code is very clear, the connect function actually does one thing, deconstructing mapStateToProps and mapDispatchToProps separately and passing them to the original component, so that we can directly use props to get the state and dispatch functions in the original component.

Considerations for using HOC

Warning-static attribute copy

When we apply HOC to enhance another component, the component we actually use is no longer the original component, so we can't get any static properties of the original component, we can manually copy them at the end of the HOC:

Function proxyHOC (WrappedComponent) {class HOCComponent extends Component {render () {return;}} HOCComponent.staticMethod = WrappedComponent.staticMethod; / /. Return HOCComponent;}

If the original component has a lot of static properties, the process is very painful, and you need to know what the static properties of all components need to be enhanced. We can use hoist-non-react-statics to help us solve this problem. It can automatically help us copy all non-React static methods, as follows:

Import hoistNonReactStatic from 'hoist-non-react-statics'; function proxyHOC (WrappedComponent) {class HOCComponent extends Component {render () {return;}} hoistNonReactStatic (HOCComponent,WrappedComponent); return HOCComponent;}

Warning-pass refs

After using high-level components, the obtained ref is actually the outermost container component, not the original component, but in many cases we need to use the ref of the original component.

High-level components cannot transmit refs transparently like transparent props. We can use a callback function to transfer ref:

Function hoc (WrappedComponent) {return class extends Component {getWrappedRef = () = > this.wrappedRef; render () {return {this.wrappedRef = ref}} {... this.props} / >;}} @ hoc class Input extends Component {render () {return}} class App extends Component {render () {return ({this.inpitRef = ref.getWrappedRef ()}} >) }}

React version 16.3 provides a forwardRef API to help us deliver refs, so the ref we obtained on the high-level component is the ref of the original component, instead of passing it manually. If your React version is greater than 16.3, you can use the following methods:

Function hoc (WrappedComponent) {class HOC extends Component {render () {const {forwardedRef,... props} = this.props; return;}} return React.forwardRef ((props, ref) = > {return;});}

Caveat-do not use higher-order components in render methods

The principles of the React Diff algorithm are:

Use the component identity to determine whether to uninstall or update the component

If the identity of the component is the same as the previous rendering, recursively update the subcomponent

If you identify a different uninstall component to remount the new component

Each call to a high-level component generates a brand new component, and the unique identification response of the component will also change. If the high-level component is called in the render method, this will cause the component to be unloaded and remounted each time.

Convention-do not change the original component

Official documentation description of high-level components:

A high-order component is a pure function with no side effects.

Let's take a look at the definition of pure functions:

If the calling parameters of the function are the same, the same result is always returned. It does not depend on any changes in state or data outside the function during program execution, but must only rely on its input parameters.

This function does not produce any observable side effects, such as network requests, input and output devices, or data mutations.

If we make changes to the original component in the high-level component, such as the following code:

InputComponent.prototype.componentWillReceiveProps = function (nextProps) {.}

This breaks our convention for high-level components and changes the original intention of using high-level components: we use high-level components to enhance rather than change the original components.

Convention-transparently transmit irrelevant props

With high-level components, we can proxy all props, but often only one or more of the props is used for a particular HOC. We need to pass through other unrelated props to the original component, such as the following code:

Function visible (WrappedComponent) {return class extends Component {render () {const {visible,... props} = this.props; if (visible = = false) return null; return;}

We only use the visible attribute to control the display of the component that can be hidden and pass on other props through.

Convention-displayName

When debugging with React Developer Tools, if we use HOC, the debugging interface can become very difficult to read, such as the following code:

@ visible class Show extends Component {render () {return I am a tag} @ visible class Title extends Component {render () {return I am a title}}

To facilitate debugging, we can manually specify a displayName for HOC. It is officially recommended to use HOCName (WrappedComponentName):

Static displayName = `Visible (${WrappedComponent.displayName}) `

This convention helps ensure the flexibility and reusability of high-level components.

Motivation for using HOC

Review the risks posed by Mixin mentioned above:

Mixin may be interdependent and coupled, which is not conducive to code maintenance.

Methods in different Mixin may conflict with each other

When Mixin is very often, components are aware of it and even have to deal with it, which can cause snowball complexity to the code.

The emergence of HOC can solve these problems:

A high-order component is a pure function with no side effects, and each high-order component will not be dependent on and coupled with each other.

High-level components can also cause conflicts, but we can avoid these behaviors while complying with the agreement

High-level components don't care about how or why the data is used, and the wrapped components don't care where the data comes from. The increase of high-level components will not increase the burden on the original components.

Defects of HOC

HOC needs to be wrapped or nested on the original components, and if you use HOC a lot, there will be a lot of nesting, which makes debugging very difficult.

HOC can hijack props, and it can also cause conflicts if you don't abide by the agreement.

Hooks

Hooks is a new feature added to React v16.7.0-alpha. It allows you to use state and other React features outside of class.

With Hooks, you can abstract logic containing state from components, which will make it easy to test. At the same time, Hooks can help you reuse this logic without rewriting the component structure. Therefore, it can also be used as a scheme to realize state logic reuse.

Read the following chapter on the motivation to use Hook. You can find that it can solve the problems caused by both Mixin and HOC.

Official Hooks

State Hook

We are going to use the class component to implement a counter function, which we might write:

Export default class Count extends Component {constructor (props) {super (props); this.state = {count: 0}} render () {return (

You clicked {this.state.count} times

{this.setState ({count: this.state.count + 1})}} > Click me)}}

With useState, we can also do this using functional components:

Export default function HookTest () {const [count, setCount] = useState (0); return (

You clicked {count} times

{setCount (count + 1); setNumber (number + 1);}} > Click me);}

UseState is a hook that adds some state to a functional component and provides a function to change those states, while receiving a parameter as the default value for the state.

Effect Hook

Effect Hook allows you to perform operations with side effect (side effects) in function components.

Parameters.

The useEffect method receives two parameters passed in:

1. Callback function: runs after the first render of the component and each subsequent update, and the React ensures that the callback will not be run until the DOM has been updated.

two。 State dependency (array): when a state dependency is configured, the callback function is called only when a state change of the configuration is detected.

UseEffect (() = > {/ / as long as the component render is executed}); useEffect (() = > {/ / only if the count changes}, [count])

Callback return value

The * arguments of useEffect can return a function, which will be called when the page renders the result of the next update and before the next useEffect is executed. This function is often used to clean up the last call to useEffect.

Export default function HookTest () {const [count, setCount] = useState (0); useEffect () = > {console.log ('execute...', count); return () = > {console.log ('cleanup...', count);}, [count]); return (

You clicked {count} times

{setCount (count + 1); setNumber (number + 1);}} > Click me);}

Execute the above code and click the button several times to get the following result:

Note that if you add in browser rendering, the result should look like this:

Page rendering. 1 execution. 1 page rendering. 2 cleanup. 1 execution. 2 page rendering. 3 cleanup. 3 page rendering. 4 cleanup. 3 execution. 4.

So why can you find the last state by performing cleanup after the browser has finished rendering? The reason is simple: what we return in useEffect is a function, which forms a closure, which ensures that the variables stored in the last function we executed will not be destroyed and contaminated.

You can try the following code that may be easier to understand.

Var flag = 1; var clean; function effect (flag) {return function () {console.log (flag);}} clean = effect (flag); flag = 2; clean (); clean = effect (flag); flag = 3; clean (); clean = effect (flag); / / execution result effect... 1 clean... 1 effect... 2 clean... 2 effect... three

Simulated componentDidMount

ComponentDidMount is equivalent to the callback of useEffect, which is executed only once after the page is initialized, which can be achieved when the second parameter of useEffect is passed in an empty array.

Function useDidMount (callback) {useEffect (callback, []);}

The above writing is not recommended by officials because it may lead to some mistakes.

Simulated componentWillUnmount

Function useUnMount (callback) {useEffect (() = > callback, []);}

Unlike effect, which is used in componentDidMount or componentDidUpdate,useEffect, it doesn't prevent browsers from rendering pages. This makes your app look smoother.

Ref Hook

With useRef Hook, you can easily get the ref of dom.

Export default function Input () {const inputEl = useRef (null); const onButtonClick = () = > {inputEl.current.focus ();}; return (Focus the input);}

Note that useRef () can not only be used to get ref, the current property of ref generated by useRef is variable, which means you can use it to save any value.

Simulated componentDidUpdate

ComponentDidUpdate is equivalent to excluding the useEffect of * calls. We can use useRef to generate an identity to record whether it is * times:

Function useDidUpdate (callback, prop) {const init = useRef (true); useEffect () = > {if (init.current) {init.current = false;} else {return callback ();}, prop);}

Considerations for using Hook

Scope of use

Hook can only be used in React functional components or custom Hook.

The main purpose of Hook is to solve a series of problems of class components, so we can use it in class components.

Declare constraint

Do not call Hook in loops, conditions, or nested functions.

Hook is implemented through an array, and each useState will change the subscript. React needs to use the call order to update the corresponding status correctly. If the useState is wrapped in a loop or conditional statement, it may cause the order of calls to be out of order, resulting in unexpected errors.

We can install an eslint plug-in to help us avoid these problems.

/ / install npm install eslint-plugin-react-hooks-- save-dev / / configure {"plugins": [/ /... "react-hooks"], "rules": {/ /. "react-hooks/rules-of-hooks": "error"}}

Custom Hook

Like HOC and mixin described above, we can also extract similar state logic from components through a custom Hook.

Customizing Hook is very simple. We only need to define a function and encapsulate the required state and effect. At the same time, Hook can also refer to each other. Name the custom Hook with the beginning of use, which makes it easy for eslint to check.

Let's take a look at a few specific Hook packages:

Log management

We can use the lifecycle Hook encapsulated above.

Const useLogger = (componentName,... params) = > {useDidMount () = > {console.log (`${componentName} initialization`, .params);}); useUnMount () = > {console.log (`${componentName} Uninstall`, .params);}) useDidUpdate (() = > {console.log (`${componentName} update`, .params);});} Function Page1 (props) {useLogger ('Page1',props); return (...)}

Modify title

Modify the page title according to different page names:

Function useTitle (title) {useEffect () = > {document.title = title; return () = > (document.title = "homepage");}, [title]);} function Page1 (props) {useTitle ('Page1'); return (...)}

Two-way binding

We extract the logic of the form onChange and encapsulate it into a Hook, so that all form components that require two-way binding can be reused:

Function useBind (init) {let [value, setValue] = useState (init); let onChange = useCallback (function (event) {setValue (event.currentTarget.value);}, []); return {value, onChange};} function Page1 (props) {let value = useBind (''); return;}

Of course, you can do the above HOC, combined with context and form to encapsulate a more general two-way binding, if you are interested, you can implement it manually.

Motivation for using Hook

Reduce the risk of state logic reuse

There are some similarities between Hook and Mixin in usage, but the logic and state introduced by Mixin can cover each other, while multiple Hook do not affect each other, so we do not need to focus on preventing conflicts of logic reuse.

Using HOC without following conventions can also lead to conflicts, such as props overrides, and so on, which can be avoided by using Hook.

Avoid hellhole nesting

Heavy use of HOC makes our code nesting at a very deep level. With HOC, we can achieve flat state logic reuse without a large number of component nesting.

Make components easier to understand

When using class components to build our program, they each have their own state, the complexity of business logic makes these components become larger and larger, each life cycle will call more and more logic, more and more difficult to maintain. Using Hook allows you to extract more common logic and split a component into smaller functions, rather than forcing segmentation based on a lifecycle approach.

Use functions instead of class

Writing a class may require more knowledge and attention than functions, such as this pointing, binding events, and so on. In addition, computers understand a class faster than a function. Hooks allows you to use more of React's new features outside of classes.

Rational choice

In fact, Hook officially released the stable version of Hook in react 16.8.0, and the author has not used it in the production environment. At present, the author uses `HOC` most in the production environment.

React officials have no intention of removing classes from React. Class components and Hook can exist at the same time. Officials also recommend avoiding any "large-scale refactoring". After all, this is a very new version, and if you like it, you can use Hook in new non-critical code.

At this point, the study of "React in-depth analysis from Mixin to HOC and then to Hook" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report