React组件应该如何封装?

泄露秘密 提交于 2020-02-23 21:57:12
  • 封装

  • 一个封装组件提供 props 控制其行为而不是暴露其内部结构。

    耦合是决定组件之间依赖程度的系统特性。根据组件的依赖程度,可区分两种耦合类型:

    当应用程序组件对其他组件知之甚少或一无所知时,就会发生松耦合。

    当应用程序组件知道彼此的许多详细信息时,就会发生紧耦合。

    松耦合是我们设计应用结构和组件之间关系的目标。 松耦合应用(封装组件)
    在这里插入图片描述
    松耦合会带来以下好处:

    可以在不影响应用其它部分的情况下对某一块进行修改。、

    任何组件都可以替换为另一种实现

    在整个应用程序中实现组件复用,从而避免重复代码

    独立组件更容易测试,增加了测试覆盖率

    相反,紧耦合的系统会失去上面描述的好处。主要缺点是很难修改高度依赖于其他组件的组件。即使是一处修改,也可能导致一系列的依赖组件需要修改。

    紧耦合应用(组件无封装)在这里插入图片描述

    封装 或 信息隐藏 是如何设计组件的基本原则,也是松耦合的关键。

    信息隐藏 封装良好的组件隐藏其内部结构,并提供一组属性来控制其行为。

    隐藏内部结构是必要的。其他组件没必要知道或也不依赖组件的内部结构或实现细节。

    React 组件可能是函数组件或类组件、定义实例方法、设置 ref、拥有 state
    或使用生命周期方法。这些实现细节被封装在组件内部,其他组件不应该知道这些细节。

    隐藏内部结构的组件彼此之间的依赖性较小,而降低依赖度会带来松耦合的好处。

    通信 细节隐藏是隔离组件的关键。此时,你需要一种组件通信的方法:props。porps 是组件的输入。 建议 prop
    的类型为基本数据(例如,string 、 number 、boolean):

    ; 必要时,使用复杂的数据结构,如对象或数组:

    <MoviesList items={[‘Batman Begins’, ‘Blade Runner’]} /> prop
    可以是一个事件处理函数和异步函数:

    prop
    甚至可以是一个组件构造函数。组件可以处理其他组件的实例化:

    function If({ component: Component, condition }) {
    return condition ? : null; } 为了避免破坏封装,请注意通过 props 传递的内容。给子组件设置
    props 的父组件不应该暴露其内部结构的任何细节。例如,使用 props 传输整个组件实例或 refs 都是一个不好的做法。

    访问全局变量同样也会对封装产生负面影响。

    案例研究:封装修复 组件的实例和状态对象是封装在组件内部的实现细节。因此,将状态管理的父组件实例传递给子组件会破坏封装。

    我们来研究一下这种情况。

    一个简单的应用程序显示一个数字和两个按钮。第一个按钮增加数值,第二个按钮减少数值:在这里插入图片描述

    核心代码:

    class App extends React.Component {
    constructor(props) {
    super(props);
    this.state = { number: 0 };
    }

    render() {
        return (
            <div className="app">
                <span className="number">{this.state.number}</span>
                <Controls parent={this} />
            </div>
        );
    } }
    

    class Controls extends React.Component {
    render() {
    return (


    <button onClick={() => this.updateNumber(+1)}>
    Increase

    <button onClick={() => this.updateNumber(-1)}>
    Decrease


    );
    }

    updateNumber(toAdd) {
        this.props.parent.setState(prevState => ({
            number: prevState.number + toAdd
        }));
    } }
    

    ReactDOM.render(, document.getElementById(‘root’)); // 问题:
    使用父组件的内部结构 class Controls extends Component {
    render() {
    return (


    <button onClick={() => this.updateNumber(+1)}>
    Increase

    <button onClick={() => this.updateNumber(-1)}>
    Decrease


    );
    }

    updateNumber(toAdd) {
        this.props.parent.setState(prevState => ({
            number: prevState.number + toAdd
        }));
    } }<Controls> 负责渲染按钮,并为其设置事件处理函数,当用户点击按钮时,父组件的状态将会被更新:number 加1或者减1 (updateNumber()方法)
    

    当前的实现有什么问题?

    第一个问题是: 的封装被破坏,因为它的内部结构在应用中传递。 错误地允许 直接去修改其
    state。

    第二个问题是: 子组件 Controls 知道了太多父组件 的内部细节,它可以访问父组件的实例,知道父组件是一个有状态组件,知道父组件的
    state 对象的细节(知道 number 是父组件 state 的属性),并且知道怎么去更新父组件的 state.
    这样就会导致: 将很难测试和重用。对 结构的细微修改会导致需要对
    进行修改(对于更大的应用程序,也会导致类似耦合的组件需要修改)。

    解决方案是设计一个方便的通信接口,考虑到松耦合和封装。让我们改进两个组件的结构和属性,以便恢复封装。

    只有组件本身应该知道它的状态结构。 的状态管理应该从 (updateNumber()方法)移到正确的位置:即
    组件中。

    被修改为 设置属性 onIncrease 和 onDecrease。这些是更新
    状态的回调函数:

    // 解决: 恢复封装 class App extends Component {
    constructor(props) {
    super(props);
    this.state = { number: 0 };
    }

    render() {
        return (
            <div className="app">
                <span className="number">{this.state.number}</span>
                <Controls
                    onIncrease={() => this.updateNumber(+1)}
                    onDecrease={() => this.updateNumber(-1)}
                />
            </div>
        );
    }
    
    updateNumber(toAdd) {
        this.setState(prevState => ({
            number: prevState.number + toAdd
        }));
    } } 现在,<Controls> 接收用于增加和减少数值的回调,注意解耦和封装恢复时:<Controls> 不再需要访问父组件实例。也不会直接去修改父组件的状态。
    

    而且, 被修改为了一个函数式组件:

    // 解决方案: 使用回调函数去更新父组件的状态 function Controls({ onIncrease, onDecrease
    }) {
    return (


    Increase
    Decrease

    ); } 组件的封装已经恢复,状态由其本身管理,也应该如此。

    此外, 不在依赖 的实现细节,onIncrease 和 onDecrease
    在按钮被点击的时候调用, 不知道(也不应该知道)这些回调的内部实现。

    组件的可重用性和可测试性显著增加。

    的复用变得很容易,因为它除了需要回调,没有其它依赖。测试也变得简单,只需验证单击按钮时,回调是否执行。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!