React -- 组件间抽象

在React组件的构建过程中,常常有这样的场景,有一类功能需要被不同的组件公用。此时就涉及到了抽象的话题。

下面我们重点讨论:mixin和高阶组件。

mixin

mixin 的目的,就是为了创造一种类似多重继承的效果,或者说,组合。

实际上,包括C++等一些年龄较大的OOP语言,都有一个强大但是危险的多重继承特性。现代语言权衡利弊,大都舍弃了它,只采用单继承。但是单继承在实现抽象的时候有很多不便,为了弥补缺失,Java引入接口(interface),其他一些语言则引入了mixin的技巧。

封装mixin方法

方法

const mixin = function(obj,mixins) {
    const newObj = obj;
    newObj.prototype = Object.create(obj.prototype);
    for (let prop in mixins) {
        if (mixins.hasOwnProperty(prop)) {
            newObj.prototype[prop] = mixins[prop]; 
        }
    }
    return newObj; 
}

应用:

const BigMixin = { 
    fly: () => {
        console.log('I can fly'); 
    }
};
const Big = function() { 
    console.log('new big');
};
const FlyBig = mixin(Big,BigMixin);
const flyBig = new FlyBig(); // => 'new big'
flyBig.fly(); // => 'I can fly'

上面这段代码实现对象混入的方法是:用赋值的方式将mixin对象里的方法都挂载到原对象上。

在React中使用mixin

React在使用createClass构建组件时提供了mixin属性,比如官方封装的:PureRenderMixin.

import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
React.createClass({
    mixins: [PureRenderMixin],render() {
        return <div>foo</div>;
    }
});

在createClass对象参数中传入数组mixins,里面封装了我们需要的模块。mixins数组也可以添加多个mixin。同时,在React中不允许出现重名普通方法的mixin。而如果是生命周期方法,则React将会将各个模块的生命周期方法叠加在一起然后顺序执行。

使用createClass实现的mixin为组件做了两件事:
1、工具方法:这是mixin的基本功能,如果你希望共享一些工具类的方法,就可以直接定义它们然后在组件中使用。
2、生命周期继承,props和state合并。mixin能够合并生命周期方法。如果有很多mixin来定义componentDidMount这个周期,那么React会很机智的将它们都合并起来执行。同样,mixin也可以作state和props的合并。

ES6 Classes和decorator

然而,当我们使用ES6 classes的形式构建组件的时候,却并不支持mixin。为了使用这个强大的功能,我们还需要采取其他方法,来达到模块重用的目的。

可以使用ES7的语法糖decorator来实现class上的mixin。

core-decorators库为开发者提供了一些实用的decorator,其中也正好实现了我们想要的@mixin。

注:decorator的知识将在下一篇博客中给出。

使用@mixin的代码

import React,{ Component } from 'React'; 
import { mixin } from 'core-decorators';

const PureRender = { 
    shouldComponentUpdate() {}
};
const Theme = { 
    setTheme() {}
};
@mixin(PureRender,Theme)
class MyComponent extends Component {
    render() {} 
}

mixin的问题

1、破坏了原有组件的封装:mixin会混入方法,给原有的组件带来新特性。但同时它也可能带来新的state和props,这意味着组件有一些“不可见”的状态需要我们去维护。另外,mixin也有可能去依赖其他的mixin,这样会建立一个mixin的依赖链,当我们改动一个mixin的状态,很有可能也会影响其他的mixin。

2、命名冲突

3、增加复杂性

针对这些困扰,React提出的新的方式来取代mixin,那就是高阶组件。

高阶组件

如果已经理解高阶函数,那么理解高阶组件也很容易的。

高阶函数:就是一种这样的函数,它接受函数作为参数输入,或者将一个函数作为返回值。例如我们常见的方法map,reduce,sort等都是高阶函数

高阶组件和和高阶函数很类似,高阶组件就是接受一个React组件作为参数输入,输出一个新的React组件。

高阶组件让我们的代码更具有复用性、逻辑性与抽象性,它可以对render方法作劫持,也可以控制props和state。

实现高阶组件的方法有如下两种:
1、属性代理:高阶组件通过被包裹的React组件来操作props。
2、反向继承:高阶组件继承于被包裹的React组件。

属性代理

示例代码

import React,{ Component } from 'React';
const MyContainer = (WrappedComponent) => 
    class extends Component {
        render() {
            return <WrappedComponent {...this.props} />;
        } 
    }

代码中我们可以看到,render方法返回了传入的WrappedComponent组件。这样,我们就可以通过高阶组件来传递props。这种方式就是属性代理。

如何使用上面这个高阶组件:

import React,{ Component } from 'React';
class MyComponent extends Component { 
    // ...
}
export default MyContainer(MyComponent);

这样组件就可以一层层的作为参数被调用,原始组件久具备了高阶组件对它的修饰。这样,保持单个组件封装的同时也保留了易用行。

功能上, 高阶组件一样可以做到像mixin对组件的控制:

1、控制props

我们可以读取、增加、编辑或是移除从WrappedComponent传进来的props。

例如:新增props

import React,{ Component } from 'React';
const MyContainer = (WrappedComponent) => 
    class extends Component {
        render() {
            const newProps = {  text: newText,};
            return <WrappedComponent {...this.props} {...newProps} />; 
        }
    }

注意:

<WrappedComponent {...this.props}/>
// is equivalent to
React.createElement(WrappedComponent,this.props,null)

这样,当调用高阶组件的时候,就可以使用text这个新的props了。

2、通过refs使用引用

3、抽象state

高阶组件可以讲原组件抽象为展示型组件,分离内部状态。

const MyContainer = (WrappedComponent) => 
    class extends Component {
        constructor(props) { 
            super(props); 
            this.state = { name: '',4 };
            this.onNameChange = this.onNameChange.bind(this); 
        }

        onNameChange(event) { 
            this.setState({
                name: event.target.value,})
        }
        render() {
            const newProps = {
                name: {
                    value: this.state.name,onChange: this.onNameChange,},}
            return <WrappedComponent {...this.props} {...newProps} />; 
        }
    }

在这个栗子中,我们把组件中对name prop 的onChange 方法提取到高阶组件中,这样就有效的抽象了同样的state操作。

使用方式:

@MyContainer
class MyComponent extends Component {
    render() {
        return <input name="name" {...this.props.name} />;
    } 
}

高阶组件和mixin的不同:

高阶组件符合函数式编程思想。对于原组件来说,并不会感知到高阶组件的存在。只需要把功能套在它之上就可以了。避免了mixin的副作用。

反向继承

const MyContainer = (WrappedComponent) => 
    class extends WrappedComponent {
        render() {
            return super.render();
        } 
    }

高阶组件返回的组件继承于WrappedComponent。

(待续)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom