Packaging of 7 Criteria for Reliable React Component Design

  Front end, html5, javascript, Programmer, react.js

Translation: Liu Xiaoxi

Original link:https://dmitripavlutin.com/7- …

The length of the original textVeryLong, but the content is too attractive to me, still can’t help but to translate it. This article is very helpful for writing reusable and maintainable React components. But because of the length is too long, I divided the article, this article focuses onEncapsulation. Due to my limited level, some translations in this article may not be accurate enough. If you have better ideas, please point them out in the comment area.

More articles to stamp: https://github.com/YvetteLau/ …

I am a dividing line.

Encapsulation

A package assembly providespropsControl their behavior rather than expose their internal structure.

Coupling is a system characteristic that determines the degree of dependency between components. Two types of coupling can be distinguished according to component dependency:

  • Loose coupling occurs when an application component knows little or nothing about other components.
  • Tight coupling occurs when application components know many details about each other.

Loose coupling is our goal in designing the relationship between application structures and components.

Loosely coupled applications (packaged components)

clipboard.png

Loose couplingIt will bring the following benefits:

  • A certain block can be modified without affecting the application of other parts. 、
  • Any component can be replaced by another implementation
  • Component reuse is implemented in the entire application program to avoid code duplication.
  • Independent components are easier to test, increasing test coverage

On the contrary, tightly coupled systems will lose the benefits described above. The main disadvantage is that it is difficult to modify components that are highly dependent on other components. Even a modification may cause a series of dependent components to need modification.

Tightly coupled applications (components not packaged)

clipboard.png

EncapsulationOrinformation hidingIt is the basic principle of how to design components and the key to loose coupling.

information hiding

A well-packaged component hides its internal structure and provides a set of attributes to control its behavior.

It is necessary to hide the internal structure. Other components do not need to know or rely on the internal structure or implementation details of the components.

ReactComponents may be function components or class components, defining instance methods, settingsrefOwnstateOr use a life cycle approach. These implementation details are encapsulated inside the component, and other components should not know these details.

Components hiding internal structures are less dependent on each other, while reducing the dependency will bring the benefit of loose coupling.

Communication

Details hiding is the key to isolating components. At this point, you need a component communication method:props.porpsIs the input to the component.

proposalpropThe type of is basic data (for example,stringnumberboolean):

<Message text="Hello world!" modal={false} />;

When necessary, use complex data structures, such as objects or arrays:

<MoviesList items={['Batman Begins', 'Blade Runner']} />

propCan be an event handler and asynchronous function:

<input type="text" onChange={handleChange} />

propIt can even be a component constructor. Components can handle instantiations of other components:

function If({ component: Component, condition }) {
    return condition ? <Component /> : null;
}
<If condition={false} component={LazyComponent} />  

In order to avoid damaging the package, please pay attention to passing throughpropsThe content delivered. Setting up subcomponentspropsThe parent component of should not expose any details of its internal structure. For example, usepropsTransfer the entire component instance orrefsIs a bad practice.

Access to global variables can also have a negative impact on packaging.

Case Study: Package Repair

The instance and state object of the component are the implementation details encapsulated inside the component. Therefore, passing a parent component instance of state management to a child component destroys the package.

Let’s look at this situation.

A simple application displays a number and two buttons. The first button increases the value and the second button decreases the value:

clipboard.png

This application consists of two components:<App>And<Controls>.

numberYes<App>ThestateObjects,<App>Responsible for rendering this number to the page.

// 问题: 封装被破坏
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 parent={this} />
            </div>
        );
    }
}

<Controls>Responsible for rendering the button and setting event handling functions for it. When the user clicks the button, the status of the parent component will be updated:numberAdd 1 or subtract 1 ((UpdateNumber () method `)

// 问题: 使用父组件的内部结构
class Controls extends Component {
    render() {
        return (
            <div className="controls">
                <button onClick={() => this.updateNumber(+1)}>
                    Increase
          </button>
                <button onClick={() => this.updateNumber(-1)}>
                    Decrease
          </button>
            </div>
        );
    }

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

What are the problems with the current implementation?

  • The first question is:<App>The package of was destroyed because its internal structure was transferred in the application.<App>To allow wrongly<Controls>To modify it directlystate.
  • The second problem is: subcomponentsControlsToo many parent components are known<App>The internal details of the, it can access the instance of the parent component, know the parent component is a stateful component, know the parent component’sstateThe details of the object (knownumberIs the parent componentstateAnd know how to update the parent componentstate.

The result of a trouble is:<Controls>It will be difficult to test and reuse. Yes<App>Minor structural changes will lead to the need for<Controls>Make modifications (for larger applications, similar coupled components will also need to be modified).

The solution is to design a convenient communication interface, taking loose coupling and encapsulation into account. Let’s improve the structure and properties of the two components to restore the encapsulation.

Only the component itself should know its state structure.<App>The state management of should start from<Controls>(updateNumber()Method) to the correct position: that is<App>In the component.

<App>Changed to<Controls>Set propertiesonIncreaseAndonDecrease. These are updates<App>Callback function for state:

// 解决: 恢复封装
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
        }));
    }
}

Now,<Controls>Receive callbacks for increasing and decreasing values, and pay attention to decoupling and package recovery:<Controls>Access to the parent component instance is no longer required. It will not directly modify the status of the parent component.

Moreover,<Controls>Modified to a functional component:

// 解决方案: 使用回调函数去更新父组件的状态
function Controls({ onIncrease, onDecrease }) {
    return (
        <div className="controls">
            <button onClick={onIncrease}>Increase</button>
            <button onClick={onDecrease}>Decrease</button>
        </div>
    );
}

<App>The encapsulation of the component has been restored and the state is managed by itself, which should be the case.

In addition,<Controls>Not dependent<App>The implementation details of,onIncreaseAndonDecreaseCalled when a button is clicked,<Controls>The internal implementation of these callbacks is unknown (and should not be known).

<Controls>The reusability and testability of components are significantly increased.

<Controls>Reuse of becomes easy because it requires no other dependencies than callback. Testing is also made simple by verifying that the callback is executed when the button is clicked.

Finally, I would like to thank all my friends who are willing to spend precious time reading this article. If this article gives you some help or inspiration, please don’t be stingy with your praise and Star. Your affirmation is my greatest motivation to move forward.https://github.com/YvetteLau/ …

I recommend paying attention to my public number.

clipboard.png