When any project develops to a certain degree of complexity, it will inevitably face the problem of logic reuse. There are usually several ways to implement logic reuse in React
Mixin
, high-end component (HOC),
decorator,
Render Props
, Hook
. This article mainly analyzes the advantages and disadvantages of the above methods to help developers make more suitable methods for business scenarios.
Mixin
This may be the first method that developers who have just switched Vue
React
Mixin
has been widely used in various object-oriented languages. is used to create an effect similar to multiple inheritance for single inheritance languages, . Although React
has now abandoned it, Mixin
was indeed React
achieve code sharing.
The generalized mixin method is to mount all the methods in the mixin object to the original object by way of assignment to achieve the mixing of objects, similar to the role of Object.assign() in ES6. The principle is as follows:
const mixin = function (obj, mixins) {
const newObj = obj
newObj.prototype = Object.create(obj.prototype)
for (let prop in mixins) {
// 遍历mixins的属性
if (mixins.hasOwnPrototype(prop)) {
// 判断是否为mixin的自身属性
newObj.prototype[prop] = mixins[prop]; // 赋值
}
}
return newObj
};
Use Mixin in React
Assuming that in our project, multiple components need to set the default name
attribute. Using mixin
can save us from writing multiple same getDefaultProps
methods in different components. We can define a mixin
:
const DefaultNameMixin = {
getDefaultProps: function () {
return {
name: "Joy"
}
}
}
In order to use mixin
, you need to add the mixins
attribute to the component, and then mixin
we wrote into an array, and use it as the attribute value of mixins
const ComponentOne = React.createClass({
mixins: [DefaultNameMixin]
render: function () {
return <h2>Hello {this.props.name}</h2>
}
})
written by mixin
can be reused in other components.
Since the mixins
is an array, it means that we can call multiple mixin
same component. In the above example, make a slight change to get:
const DefaultFriendMixin = {
getDefaultProps: function () {
return {
friend: "Yummy"
}
}
}
const ComponentOne = React.createClass({
mixins: [DefaultNameMixin, DefaultFriendMixin]
render: function () {
return (
<div>
<h2>Hello {this.props.name}</h2>
<h2>This is my friend {this.props.friend}</h2>
</div>
)
}
})
we can even in a mixin
contains other's mixin
.
For example, write a new mixin
`DefaultProps containing the above DefaultNameMixin
and DefaultFriendMixin`:
const DefaultPropsMixin = {
mixins: [DefaultNameMixin, DefaultFriendMixin]
}
const ComponentOne = React.createClass({
mixins: [DefaultPropsMixin]
render: function () {
return (
<div>
<h2>Hello {this.props.name}</h2>
<h2>This is my friend {this.props.friend}</h2>
</div>
)
}
})
At this point, we can conclude that mixin
at least the following advantages:
- can use the same
mixin
in multiple components; -
mixin
in the same component; - may be the same
mixin
nested in a pluralitymixin
;
But in different scenarios, advantages may also become disadvantages:
- destroy the original package assembly, may need to maintain a new
state
andprops
like state ; - different from
mixin
. The naming in 0609b51c13c2eb is unknowable, and it is very easy to conflict ; - may have recursive call problems, which increases the complexity of the project and the difficulty of maintenance ;
In addition, mixin
has its own processing logic for issues such as state conflicts, method conflicts, and the calling sequence of multiple life cycle methods. Interested students can refer to the following articles:
High-end components
Since mixin
these limitations, it React
stripped mixin
, instead higher order components to replace it.
higher-order component is essentially a function, which accepts a component as a parameter and returns a new component
.
React
high-end components when implementing some public components, such as
react-router
in withRouter
, and Redux
in connect
. Take withRouter
as an example.
By default, this.props
Route
route matching must exist to have 0609b51c13c524 and only have routing parameters, in order to use
functional navigation to execute
this.props.history.push('/next')
jump to the page of the corresponding route. higher order components
withRouter
action is not a Route
assembly wrapped route package to Route
inside, thereby react-router
three objects history
, location
, match
put into the assembly props
properties, it can be realized so Functional navigation jump.
The realization principle of withRouter
const withRouter = (Component) => {
const displayName = `withRouter(${Component.displayName || Component.name})`
const C = props => {
const { wrappedComponentRef, ...remainingProps } = props
return (
<RouterContext.Consumer>
{context => {
invariant(
context,
`You should not use <${displayName} /> outside a <Router>`
);
return (
<Component
{...remainingProps}
{...context}
ref={wrappedComponentRef}
/>
)
}}
</RouterContext.Consumer>
)
}
Use code:
import React, { Component } from "react"
import { withRouter } from "react-router"
class TopHeader extends Component {
render() {
return (
<div>
导航栏
{/* 点击跳转login */}
<button onClick={this.exit}>退出</button>
</div>
)
}
exit = () => {
// 经过withRouter高阶函数包裹,就可以使用this.props进行跳转操作
this.props.history.push("/login")
}
}
// 使用withRouter包裹组件,返回history,location等
export default withRouter(TopHeader)
Since the essence of the high-level component
getting the component and returning the new component, it can theoretically realize multiple nesting
mixin
E.g:
Write a higher-order function that empowers singing
import React, { Component } from 'react'
const widthSinging = WrappedComponent => {
return class HOC extends Component {
constructor () {
super(...arguments)
this.singing = this.singing.bind(this)
}
singing = () => {
console.log('i am singing!')
}
render() {
return <WrappedComponent />
}
}
}
Write a higher-order function that empowers dancing
import React, { Component } from 'react'
const widthDancing = WrappedComponent => {
return class HOC extends Component {
constructor () {
super(...arguments)
this.dancing = this.dancing.bind(this)
}
dancing = () => {
console.log('i am dancing!')
}
render() {
return <WrappedComponent />
}
}
}
Use the above high-end components
import React, { Component } from "react"
import { widthSing, widthDancing } from "hocs"
class Joy extends Component {
render() {
return <div>Joy</div>
}
}
// 给Joy赋能唱歌和跳舞的特长
export default widthSinging(withDancing(Joy))
It can be seen from the above that just a simple package with a high-order function can turn the originally simple Joy into a little nightclub prince who can both sing and dance!
Use HOC conventions
When using HOC
, there are some conventional conventions:
- Pass irrelevant Props to the packaging component (pass props that have nothing to do with their specific content);
- Step-by-step combination (avoid different forms of HOC serial calls);
- Contains the displayed displayName to facilitate debugging (each HOC should conform to the rule's display name);
- Do not
render
function (each time you render, the higher-order returns a new component, which affects the diff performance); - The static method must be copied (the new component returned by the higher order will not contain the static method of the original component);
- Avoid using ref (ref will not be passed);
Advantages and disadvantages of HOC
So far we can summarize the advantages of high-order components (HOC):
HOC
is a pure function, easy to use and maintain;- Also, since
HOC
is a pure function, it supports the input of multiple parameters to enhance its scope of application; HOC
returns a component, which can be combined and nested and has strong flexibility;
Of course, HOC
also has some problems:
- When a plurality
HOC
when nested, can not be directly determined subassemblyprops
which fromHOC
responsible for transmission; - When the parent and child components have the same name
props
, it will cause the parent component to overwrite the child component with the same nameprops
, andreact
will not report an error, and the developer's perception is low; - Each
HOC
returns a new component, which produces a lot of useless components, and at the same time deepens the component level, which is not convenient for troubleshooting;
modifier and the
high-end components belong to the same mode, so I will not discuss them here.
Render Props
Render Props
is a very flexible and highly reusable mode. It can encapsulate specific behaviors or functions into a component, and provide it to other components for use by other components.
The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.
This is the official React
Render Props
, which is translated into the vernacular: " Render Props
is a technology to React Components
props
component contains a props
attribute to implement the attribute of the function
Internal rendering logic".
Official example:
<DataProvider render={(data) => <h1>Hello {data.target}</h1>} />
As above, the DataProvider
component has a props
render
(also can be called other names), which is a function, and this function returns a React Element
, which is used to complete the rendering by calling this function inside the component. render props
technology.
Readers may wonder, "Why do we need to call the props
property to achieve internal rendering of the component, instead of directly completing the rendering in the component"? Borrowing React
official reply, render props
not every React
when developers need to master the skills, you may never even use this method, but it is there really for developers thinking about code-sharing component of the problem, providing One more choice.
Render Props
usage scenarios
We may need to frequently use pop-up windows in project development. The pop-up window UI can be ever-changing, but the functions are similar, that is, open and
closed. Take
antd
as an example:
import { Modal, Button } from "antd"
class App extends React.Component {
state = { visible: false }
// 控制弹窗显示隐藏
toggleModal = (visible) => {
this.setState({ visible })
};
handleOk = (e) => {
// 做点什么
this.setState({ visible: false })
}
render() {
const { visible } = this.state
return (
<div>
<Button onClick={this.toggleModal.bind(this, true)}>Open</Button>
<Modal
title="Basic Modal"
visible={visible}
onOk={this.handleOk}
onCancel={this.toggleModal.bind(this, false)}
>
<p>Some contents...</p>
</Modal>
</div>
)
}
}
The above is the simplest Model
. Even if it is simple to use, we still need to pay attention to its display status and realize its switching method. But developers actually only want to pay attention to onOk
related to business logic. The ideal way to use it should be like this:
<MyModal>
<Button>Open</Button>
<Modal title="Basic Modal" onOk={this.handleOk}>
<p>Some contents...</p>
</Modal>
</MyModal>
The above usage can be achieved through render props
import { Modal, Button } from "antd"
class MyModal extends React.Component {
state = { on: false }
toggle = () => {
this.setState({
on: !this.state.on
})
}
renderButton = (props) => <Button {...props} onClick={this.toggle} />
renderModal = ({ onOK, ...rest }) => (
<Modal
{...rest}
visible={this.state.on}
onOk={() => {
onOK && onOK()
this.toggle()
}}
onCancel={this.toggle}
/>
)
render() {
return this.props.children({
Button: this.renderButton,
Modal: this.renderModal
})
}
}
In this way, we have completed a Modal
Modal
on other pages, we only need to pay attention to the specific business logic.
As can be seen above, render props
is a real React
components, rather than HOC
as just a can return function component , this also means that render props
not like HOC
the same generation assembly-level nested problem, do not worry props
Overwrite problems caused by naming conflicts.
render props
usage restrictions
In render props
should avoid using arrow function, as this will affect performance.
such as:
// 不好的示例
class MouseTracker extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
)
}
}
It is not good to write this way, because the render
method is likely to be rendered multiple times. Using the arrow function
render
to be different each time it is rendered, but there is actually no difference, which will lead to Performance issues.
So a better way is to render
as an instance method, so that even if we render multiple times, the binding is always the same function.
// 好的示例
class MouseTracker extends React.Component {
renderCat(mouse) {
return <Cat mouse={mouse} />
}
render() {
return (
<Mouse render={this.renderTheCat} />
)
}
}
The advantages and disadvantages of render props
advantage
- The naming of props can be modified, and there is no mutual coverage;
- Know the source of props;
- There will be no multi-level nesting of components;
Disadvantage
- Cumbersome writing;
- Cannot access data outside the
return
Easy to produce function callback nesting;
The following code:
const MyComponent = () => { return ( <Mouse> {({ x, y }) => ( <Page> {({ x: pageX, y: pageY }) => ( <Connection> {({ api }) => { // yikes }} </Connection> )} </Page> )} </Mouse> ) }
Hook
React
is components. Therefore, React
has been committed to optimizing and perfecting the way of declaring components. From the earliest class components to
function components, each has its own advantages and disadvantages.
class component can provide us with a complete life cycle and state (state), but it is very cumbersome in writing.
function component is very simple and light, its limitation is that must be a pure function and cannot contain state. is also not supported, so the
type component cannot replace the
function component.
The React
team felt that component should be a function, not the class , which resulted in React Hooks
.
React Hooks is to enhance the functional components. You can write a fully functional component without using "classes" at all.
Why is it said that class components are "cumbersome", borrowing the official example of
React
import React, { Component } from "react"
export default class Button extends Component {
constructor() {
super()
this.state = { buttonText: "Click me, please" }
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(() => {
return { buttonText: "Thanks, been clicked!" }
})
}
render() {
const { buttonText } = this.state
return <button onClick={this.handleClick}>{buttonText}</button>
}
}
The above is a simple button component, including the most basic state and click method. After clicking the button, the state changes.
This is a very simple functional component, but it requires a lot of code to implement. Since the function component does not contain state, we cannot use the
function component to declare a component with the above functions. But we can use
Hook
to achieve:
import React, { useState } from "react"
export default function Button() {
const [buttonText, setButtonText] = useState("Click me, please")
function handleClick() {
return setButtonText("Thanks, been clicked!")
}
return <button onClick={handleClick}>{buttonText}</button>
}
In comparison, Hook
appears to be lighter, and it retains its own state while function component.
In the above example, the first hook useState()
is introduced. In addition, the React
official also provides hooks such as useEffect()
, useContext()
, useReducer()
For specific hooks and their usage details, please see official .
Hook
is that in addition to the official basic hooks, we can also use these basic hooks to encapsulate and customize the hooks, so as to achieve easier code reuse.
Hook pros and cons
advantage
- Easier to reuse code;
- Refreshing code style;
- Less code;
Disadvantage
- State is not synchronized (functions run independently, each function has an independent scope)
- Need more reasonable use
useEffect
- The granularity is small, and a lot of complex logic needs to be abstracted
hook
to sum up
Except that Mixin
is slightly behind because of its own obvious defects, for high-end components,
render props
, react hook
, there is no way to call the best solution for . They all have advantages and disadvantages. Even the most popular
react hook
, although each hook
looks so short and refreshing, but in actual business, usually one business function corresponds to multiple hook
, which means that when the business changes, you need to go Maintaining multiple hook
changes, compared to maintaining one class
, the mental burden may increase a lot. 's own business is the best solution for 1609b51c13d5ad.
Reference documents:
- React Mixin
- Mixins Considered Harmful
- Higher-Order Components
- Render Props
- React Supplement: Render Props and its usage scenarios
- Hook Introduction
Welcome to follow the blog of Bump Lab: aotu.io
Or follow the AOTULabs official account (AOTULabs) and push articles from time to time.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。