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

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

640 (3).jpg


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


原文:https://segmentfault.com/a/1190000019153177

译者:博轩


什么是高阶组件?


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


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


它做了什么?


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


什么时候去使用?


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


栗子:


  • 挂载,订阅数据


  • UI 增加交互(也可以使用容器组件,或者 Render Props)


  • 排序,过滤输入的数据


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


一个愚蠢的例子


我们有一个 Mouse 组件。


const Mouse = () => (
    <span className="mouse" role="img">🐭</span>
)


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


class Mouse extends Component {
    componentDidMount = () => new Draggable(this.ELEMENT)
    render = () => (
        <span className="mouse" ref={e => (this.ELEMENT = e)} role="img">🐭</span>
    )
}


640 (4).jpg


我们加一只 🐱


const Cat = () => (
    <span className="cat" role="img">🐱</span>
)


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


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 组件上吧 👍


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>
    )
}


640 (5).jpg


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


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;
}


const Mouse = () => (
    <span className="mouse" role="img">
        🐭
        {x !== undefined && 
            y !== undefined && (
                <span className="mouse__position"> {`(${x}, ${y})`} </span>
        )}
    </span>
)


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


640 (6).jpg


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


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 相互替换使用


这就是一篇关于高阶组件的简短介绍 ~

目录
相关文章
|
26天前
|
前端开发 开发者
React 函数组件与类组件对比
【10月更文挑战第4天】本文详细比较了React中的函数组件与类组件。函数组件是一种简单的组件形式,以纯函数的形式返回JSX,易于理解与维护,适用于简单的UI逻辑。类组件则是基于ES6类实现的,需要重写`render`方法并能利用更多生命周期方法进行状态管理。文章通过示例代码展示了两者在状态管理与生命周期管理上的差异,并讨论了常见的问题如状态更新异步性与生命周期管理的复杂性,最后给出了相应的解决方法。通过学习,开发者可以根据具体需求选择合适的组件类型。
49 8
|
24天前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
125 2
|
2月前
|
前端开发 JavaScript 网络架构
react对antd中Select组件二次封装
本文介绍了如何在React中对Ant Design(antd)的Select组件进行二次封装,包括创建MSelect组件、定义默认属性、渲染Select组件,并展示了如何使用Less进行样式定义和如何在项目中使用封装后的Select组件。
74 2
react对antd中Select组件二次封装
|
4天前
|
前端开发 JavaScript 安全
学习如何为 React 组件编写测试:
学习如何为 React 组件编写测试:
15 2
|
11天前
|
前端开发 JavaScript 测试技术
React 高阶组件 (HOC) 应用
【10月更文挑战第16天】高阶组件(HOC)是 React 中一种复用组件逻辑的方式,通过接受一个组件并返回新组件来实现。本文介绍了 HOC 的基础概念、核心功能和常见问题,包括静态方法丢失、ref 丢失、多个 HOC 组合和 props 冲突的解决方案,并提供了具体的 React 代码示例。通过本文,读者可以更好地理解和应用 HOC,提高代码的复用性和可维护性。
34 8
|
11天前
|
缓存 前端开发 JavaScript
前端serverless探索之组件单独部署时,利用rxjs实现业务状态与vue-react-angular等框架的响应式状态映射
本文深入探讨了如何将RxJS与Vue、React、Angular三大前端框架进行集成,通过抽象出辅助方法`useRx`和`pushPipe`,实现跨框架的状态管理。具体介绍了各框架的响应式机制,展示了如何将RxJS的Observable对象转化为框架的响应式数据,并通过示例代码演示了使用方法。此外,还讨论了全局状态源与WebComponent的部署优化,以及一些实践中的改进点。这些方法不仅简化了异步编程,还提升了代码的可读性和可维护性。
|
22天前
|
前端开发 JavaScript 调度
React 组件状态(State)
10月更文挑战第8天
13 1
|
2月前
|
前端开发
React给antd中TreeSelect组件左侧加自定义图标icon
本文介绍了如何在React中为Ant Design的TreeSelect组件的每个树节点添加自定义图标,并解决了因缺少key属性而导致的警告问题,展示了如何通过递归函数处理treeData数据并为每个节点添加图标。
61 2
React给antd中TreeSelect组件左侧加自定义图标icon
|
2月前
|
前端开发
React添加路径别名alias、接受props默认值、并二次封装antd中Modal组件与使用
本文介绍了在React项目中如何添加路径别名alias以简化模块引入路径,设置组件props的默认值,以及如何二次封装Ant Design的Modal组件。文章还提供了具体的代码示例,包括配置Webpack的alias、设置defaultProps以及封装Modal组件的步骤和方法。
63 1
React添加路径别名alias、接受props默认值、并二次封装antd中Modal组件与使用
|
29天前
|
JavaScript 前端开发 安全
使用 TypeScript 加强 React 组件的类型安全
【10月更文挑战第1天】使用 TypeScript 加强 React 组件的类型安全
37 3