关于HOC、RenderProps、Hooks和使用场景

本文会用实际列子对三者进行比较

1. HOC

  • 定义:高阶组件是接收一个组件为参数,返回新组件的组件。
  • 优点:提取公共逻辑,降低耦合度。

例子: 现在有两个公共组件,分别是 处理QQ、手机号码的组件。

// qq 组件
import React, { Component } from 'react'
export default WrappedComponent => {
    return class extends Component {
        constructor(props) {
            super(props.state)
            this.state = {
                qq: 1234567890
            }
        }
        setQQ = qq => {
            this.setState({ qq: qq })
        }
        render() {
            return (
                <div>
                    <WrappedComponent setQQ={this.setQQ} {...this.state} {...this.props} />
                </div>
            )
        }
    }
}

// phone 组件
import React, { Component } from 'react'
export default WrappedComponent => {
    return class extends Component {
        constructor(props) {
            super(props.state)
            this.state = {
                phone: 13427767788
            }
        }
        setPhone = phone => {
            this.setState({ phone: phone })
        }
        render() {
            return (
                <div>
                    <WrappedComponent setPhone={this.setPhone} {...this.state} {...this.props} />
                </div>
            )
        }
    }
}

// MyForm
import React from 'react'

import QQHOC from '@shared/Common/QQ'
import PhoneHOC from '@shared/Common/Phone'
class MyForm extends React.Component<any> {
    render() {
        console.log(this.props)         
        // {"qq":1234567890,"phone":13427767788,setPhone:f(),setQQ:f()}
        return (
            <div>
                <div
                    onClick={() => {
                        this.props.getQQ('123123123123123')
                    }}
                >
                    点击改变 QQ:{this.props.qq}
                </div>
                <div
                    onClick={() => {
                        this.props.getPhone('123123123123123')
                    }}
                >
                    点击改变 Phone:{this.props.phone}
                </div>
            </div>
        )
    }
}
export default PhoneHOC(QQHOC(MyForm))

上面MyForm组件使用了QQPhone高阶组件,可以发现以下问题

  • 缺点:
    ①.使用多个高阶组件会一层套一层,即使使用装饰器也不优雅。
    ②.state、内部方法相同的话会进行覆盖,除非每个组件定义一个唯一的对象包裹需要传递的内容。
    ③.溯源不清晰,难清晰知道这个参数来自哪里的。

2.render props

  • 定义:组件接收一个函数,这个函数获取组件的state实现渲染逻辑。
  • 优点:清楚知道这个state来自哪里。

同样用上面例子

// qq 组件
import React, { Component } from 'react'
export default class RenderPropsQQ extends Component<any, any> {
    constructor(props) {
        super(props.state)
        this.state = {
            qq: 13427767788
        }
    }

    setQQ = qq => {
        this.setState({ qq: qq })
    }
    render() {
        return <div>{this.props.render({ state: this.state, setQQ: this.setQQ })}</div>
    }
}
// phone 组件
import React, { Component } from 'react'
export default class RenderPropsPhone extends Component<any, any> {
    constructor(props) {
        super(props.state)
        this.state = {
            //定义可复用的状态
            phone: 13427767788
        }
    }
    setPhone = phone => {
        this.setState({ phone: phone })
    }
    render() {
        return <div>{this.props.render({ state: this.state, setPhone: this.setPhone })}</div>
    }
}

// MyForm
import * as React from 'react'

import RenderPropsQQ from '@shared/Common/RenderPropsQQ'
import RenderPropsPhone from '@shared/Common/RenderPropsPhone'
const MyForm = () => {
    return (
        <div>
            <RenderPropsPhone
                render={({ state: phoneState, setPhone }) => {
                    return (
                        <RenderPropsQQ
                            render={({ state: qqState, setQQ }) => {
                                return (
                                    <>
                                        <div
                                            onClick={() => {
                                                setQQ('123123123123123')
                                            }}
                                        >
                                            点击改变 QQ:{qqState.qq}
                                        </div>
                                        <div
                                            onClick={() => {
                                                setPhone('123123123123123')
                                            }}
                                        >
                                            点击改变 Phone:{phoneState.phone}
                                        </div>
                                    </>
                                )
                            }}
                        />
                    )
                }}
            />
        </div>
    )
}
export default MyForm

  • 缺点:
    ①.使用起来会嵌套地狱。
    ②.不能在return外使用数据。例如,我想监听QQ号码的变化,我只能通在过组件内部提供相应的回调函数。

3.hook

  • 定义:让我在不编写class的情况下可以使用state。

还是上面的例子

// qq 组件
import { useState } from 'react'

export default function QQHook() {
    const [qq, setQQ] = useState('123123123123')
    const handleSetQQ = val => {
        setQQ(val)
    }
    return { qq, setQQ: handleSetQQ }
}
// phone 组件
import { useState } from 'react'

export default function PhoneHook() {
    const [phone, setPhone] = useState('123123123123')
    const handleSetPhone = val => {
        setPhone(val)
    }
    return { phone, setPhone: handleSetPhone }
}
// myFrom
import * as React from 'react'

import useQQHook from '@shared/Common/QQHook'
import usePhoneHook from '@shared/Common/PhoneHook'

const TestHook: React.FC = () => {
    const { qq, setQQ } = useQQHook()
    const { phone, setPhone } = usePhoneHook()

    return (
        <div>
            <div
                onClick={() => {
                    setQQ('123123123123123')
                }}
            >
                点击改变 QQ:{qq}
            </div>
            <div
                onClick={() => {
                    setPhone('123123123123123')
                }}
            >
                点击改变 Phone:{phone}
            </div>
        </div>
    )
}
export default TestHook

  • 解决了
    ①:命名冲突,我可以进行重命名。
    ②:清晰标注来源。
    ③:可以监听变化。
    ④:不存在嵌套。

存在即合理

HOC 跟 renderProps也有它的存在理由的。
①.如果组件里面还有其他渲染,不纯粹是处理state的时候可以用HOC。
②.至于renderProps我举个真实场景,我有很多个按钮,每个按钮点击会打开不同的Dialog。一般做法的,通过定义多个visible的state控制Dialog,如下

...
 const [visible1,setVisible1] = useState(false)
 const [visible2,setVisible2] = useState(false)

return (
  <div>
    <Button onClick={()=>setVisible1(true)}>弹框一</Button>
    <Button onClick={()=>setVisible2(true)}>弹框二</Button>
    {visible1 && <Dialog1 />}
    {visible2 && <Dialog2 />}
  </div>
)
...
  • 问题
    ①.每次都重复写Dialog。
    ②.无法立刻找到Dialog对应是哪个Button控制的。

  • 解决
    封装一个ButtonDialog,传递触发Button和通过回调的handleShow实现打开Dialog,Dialog具体内容通过children进行传递。

// ButtonDialog
...
return (
   <span>
         {button(this.show)}
         <Modal {...modalProps}>{children}</Modal>
   </span>
)
...
// 调用
return (
   <ButtonModal
    modalProps={modalProps}
    button={handleShow => (
      <Button
        onClick={() => {
          handleShow()
        }}
      >
         Button1
      </Button>
    )}
  >
    <DialogContent />
  </ButtonModal>
)

  • 解决
    ①.不需要创建多个Dialog,只需要关注Dialog具体的内容。
    ②.控制Dialog的state放在组件内部,更加简洁。
    ③.哪个Button对应哪个Dialog一目了然。

总结

①.纯粹是复用state,复用多个组件 用hook
②.使用单个组件,并且组件有自己的渲染内容可以用HOC
③.状态不需要外部使用而且只用单个组件,可以用render props

(PS:具体问题具体分析,还是要多做比较,书上得来终觉浅 绝知此事要躬行。)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Hooks 是React的一次革命性升级,本文将对其优势和API进行比较全面的解析 为什么要有hooks 在没有h...
    smartzheng阅读 928评论 0 5
  • react rendering原理 举个栗子 以上程序,react是这样渲染的。 每一次渲染中,count的值是固...
    初漾流影阅读 2,266评论 0 2
  • 重复是不可能的,这辈子都不可能写重复的代码。 当然,这句话分分钟都要被打脸。我们烦恼于频繁的增加需求。 虽然我们不...
    7revor阅读 1,058评论 0 1
  • 1. React简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScrip...
    王蕾_fd49阅读 407评论 0 0
  • 前言 React 在 v16.8 的版本中推出了 React Hooks 新特性。在我看来,使用 React Ho...
    觞O阅读 2,280评论 0 7