前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【译】三分钟掌握 React 高阶组件

【译】三分钟掌握 React 高阶组件

作者头像
桃翁
发布2019-06-18 12:12:18
5860
发布2019-06-18 12:12:18
举报
文章被收录于专栏:前端桃园

阅读本文大约需要 5 分钟

掌握这个有用的模式,停止在 React Components 中重复逻辑! ?

原文:https://segmentfault.com/a/1190000019153177 译者:博轩

什么是高阶组件?

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

译注:对,我又一次借鉴了官网 ?

它做了什么?

他们接收一个组件并返回一个新的组件!

什么时候去使用?

当你的组件之间出现重复的模式 / 逻辑的时候。

栗子:

  • 挂载,订阅数据
  • UI 增加交互(也可以使用容器组件,或者 Render Props)
  • 排序,过滤输入的数据

译注:第三个说法,我个人可能更加倾向于在传入组件之前做处理,而不是使用高阶组件

一个愚蠢的例子

我们有一个 Mouse 组件。

代码语言:javascript
复制
const Mouse = () => (
    <span className="mouse" role="img">?</span>
)

接下来,让我们使用 GreenSock’s Draggable 模块,来让组件变的可以拖拽。

代码语言:javascript
复制
class Mouse extends Component {
    componentDidMount = () => new Draggable(this.ELEMENT)
    render = () => (
        <span className="mouse" ref={e => (this.ELEMENT = e)} role="img">?</span>
    )
}

我们加一只 ?

代码语言:javascript
复制
const Cat = () => (
    <span className="cat" role="img">?</span>
)

这个组件同样需要变得可拖拽✋,接下来,让我们使用高阶组件(HOC)来试一下:

代码语言:javascript
复制
const withDrag = Wrapped => {
    class WithDrag extends Component {
        componentDidMount = () => new Draggable(this.ELEMENT)
        render = () => {
            return (
                <span className="draggable_wrapper" ref={e => (this.ELEMENT = e)}>
                    <Wrapped  {...this.props} />
                </span>
            )
        }
    }
    WithDrag.displayName = `WithDrag(${Wrapped.displayName || Wrapped.name})`
    return WithDrag;
}

我们的高阶组件(HOC)可以通过 props 接受一组件,并返回一个新的组件。

许多高阶组件会在传递组件的过程中,注入新的 props 。这通常是决定我们是否应该使用高阶组件的因素之一。如果,我们不注入 props ,我们可以使用一个容器组件,或者 Render Props。

对于我们的高阶组件(HOC),我们也可以使用 Render Props 来达到相同的效果。你可能会觉得使用 HOC 来实现并不合适。但是,这个 “愚蠢的例子” 会让你更加熟悉 HOC 。 这比注入数据的示例更加有趣!?

让我们将这个 HOC 应用到 CatMouse 组件上吧 ?

代码语言:javascript
复制
const Mouse = () => (
    <span className="mouse" role="img">?</span>
)
const Cat = () => (
    <span className="cat" role="img">?</span>
)
const DraggableMouse = withDrag(Mouse)
const DraggableCat = withDrag(Cat)

class App extends Component {
    render = () => (
        <Fragment>
            <DraggableMouse />
            <DraggableCat />
        </Fragment>
    )
}

image

接下来,让我们在高阶组件中增加 onDrag 回调函数,并在 props 中注入 xy 的位置属性。

代码语言:javascript
复制
const withDrag = Wrapped => {
    class WithDrag extends Component {
        state = {
            x: undefined,
            y: undefined,
        }
        componentDidMount = () => new Draggable(this.ELEMENT, { onDrag: this.onDrag })
        onDrag = e => {
            const { x, y } = e.target.getBoundingClientRect();
            this.setState({ x: Math.floor(x), y: Math.floor(y) })
        }
        render = () => (
            <span className="draggable_wrapper" ref={e => (this.ELEMENT = e)}>
                <Wrapped  {...this.props} x={this.state.x} y={this.state.y} />
            </span>
        )
    }
    WithDrag.displayName = `WithDrag(${Wrapped.displayName || Wrapped.name})`
    return WithDrag;
}
代码语言:javascript
复制
const Mouse = () => (
    <span className="mouse" role="img">
        ?
        {x !== undefined && 
            y !== undefined && (
                <span className="mouse__position"> {`(${x}, ${y})`} </span>
        )}
    </span>
)

现在 Mouse 组件会向用户展示他的 XY 位置属性 ?

image

我们也可以给 HOC 传递 props。然后在传递的过程中过滤掉这些无用的属性。举个例子,传递一个 onDrag 回调函数。

代码语言:javascript
复制
const withDrag = Wrapped => {
    class WithDrag extends Component {
        componentDidMount = () => new Draggable(this.ELEMENT, { onDrag: this.props.onDrag })
        render = () => {
            const { onDrag, ...passed } = this.props;
            return (
                <span className="draggable__wrapper" ref={e => (this.ELEMENT = e)}>
                    <Wrapped  {...passed} />
                </span>
            )
        }
    }
    WithDrag.displayName = `WithDrag(${Wrapped.displayName || Wrapped.name})`
    return WithDrag;
}

class App extends Component {
    render = () => (
        <Fragment>
            <DraggableMouse
                onDrag={e => console.info(e.target.getBoundingClientRect())}
            />
        </Fragment>
    )
}

通过使用 HOC ,我们的组件仍然很简单,复杂的逻辑都交给 HOC 来处理了。 我们的组件只关心传递给他们的内容。 我们可以在其他地方重复使用它们而且不会有可以被拖拽的属性。这使得我们的应用更容易维护。

优秀的实践 ?

  • 当出现重复的模式的时候,使用它们
  • 为了方便调试,需要更新处理之后组件的 displayName
  • 传递与当前 HOC 无关的所有 props

糟糕的实践 ?

  • 过度使用,其他模式可能会更加适合
  • 改变原始组件
  • render 方法中使用高阶组件

译注:永远不要在 React render() 方法中定义 React 组件(甚至是无状态组件)。React 在每次更新状态的时候,都会废弃旧的 html DOM 元素并将其替换为全新的元素。比如在 render() 函数中定义一个输入组件,元素被替换之后就会失去焦点,每次只能输入一个字符。

注意 ?

  • Refs 不会被传递
  • 务必复制静态方法
  • 大部分 HOC 都可以和 render props 相互替换使用
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端桃园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是高阶组件?
  • 它做了什么?
  • 什么时候去使用?
  • 一个愚蠢的例子
  • 优秀的实践 ?
  • 糟糕的实践 ?
  • 注意 ?
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档