Since I encountered some problems in the process of encapsulating components in React recently, I carefully understood and wrote down some problems that should be involved and considered in the process of encapsulating components in React. (The following is mainly for THE UI components, due to the limited level of the content does not guarantee correctness, just some personal thoughts)

What is a component

Components can break up the UI into separate, reusable parts so that you can focus only on building each individual part.

Components are UI parts that are packaged and have independent functions.

In React, everything is a component, so it’s important to understand the workflow and core of a component.

React has a variety of ways to create components and a variety of component concepts. Therefore, it is worth thinking deeply about which component creation method should be used and which component should be designed when designing components.

What characteristics should a component have in React? React considers that components should have the following characteristics:

  1. Composeable: A component is easy to use with other components, or nested within another component. If a component creates another component internally, the parent component owns the child component it creates. By this feature, a complex UI can be split into simple UI components.
  2. Reusable: Each component is functional and can be used in multiple UI scenarios.
  3. Maintainable: Each small component contains only its own logic and is easier to understand and maintain;

What features should a well-designed component have?

(1) High cohesion, low coupling

We often talk about a well-designed system being highly cohesive and low coupled, but I think a good component should also be highly cohesive and low coupled.

So how do we make a component achieve high cohesion and low coupling?

  1. High cohesion: Putting logically closely related content into one component. React is inherently highly cohesive because it can put the JSX that presents the content, the JavaScript code that defines the behavior, and even the CSS that defines the style in a single JavaScript file.
  2. Low coupling: Dependencies between different components should be minimized. That is, each component should be as independent as possible. One component should not master the details of the other components, but should try to know little or nothing about the other components.

Why do you need low coupling?

Because low coupling provides the following benefits:

  1. A change in one part of the system does not affect the rest
  2. Any component can be replaced by a substitute
  3. Components can be reused between systems
  4. You can easily test individual components, improving your application’s test code coverage

The problem with highly coupled components is that it is impossible or difficult to modify a component that relies heavily on other components, or even to change a single field used to pass data, resulting in a lot of changes.

(2) Hide the internal structure

A well-packaged component should hide its internal structure and provide a way to control its behavior through a set of props.

It is necessary to hide the internal structure. Internal structure or implementation details should not be known or associated by other components.

The React component can be functional or class-based and can define instance methods, set refs, maintain state, or use lifecycle methods. These implementation details are encapsulated in the component itself, and no other component should see any of them.

Components designed based on this feature have very little dependence on other components, bringing the benefits of low coupling.

(3) Single responsibilities

I think components should be in line with the single responsibility principle, a component should be responsible for only one thing, and do it well, because I think if a component is responsible for handling things too much, when modified one thing is likely also affects it’s responsible for other things, and not conducive to maintain and reuse.

What should you think about when encapsulating a component?

  1. What should this component do
  2. The component should at least need to know that information
  3. What does this component report back

When designing a component, we should not be limited to implementing current requirements and designing a component that is suitable for a single project, but should be a common component that can accommodate most of the same requirements. So when we meet a requirement, we should first abstract the requirement, not roll up our sleeves when we see the design draft.

For example, when we meet the requirement of a multicast graph component, we can split the following requirement and get:

(1) What does this component do:

  1. Multiple images can be displayed
  2. You can flip left to right, or you can flip up and down
  3. PageControl’s state changes as the image scrolls and there may be hidden requirements such as:
  4. Should support left and right or up and down infinite loop scrolling
  5. Optional automatic rotation
  6. Support manual slide to switch pictures
  7. Images have click events that can be clicked to perform related event reactions

(2) What is the least this component should know

A good component should work like magic, requiring very few parameters and conditions to get the desired effect. As with the caroute diagram component, the component should know at least:

  1. An array of url addresses for the image
  2. A placeholder for when the image does not exist

Other information that may or may not be known may include:

  1. Whether to enable automatic multicast. The default value is yes or no

  2. Scroll left or right or up or down. Default is left or right

    And so on…

(3) What does this component respond to

A usable rotation map effect

4. Communication of components

Parent components communicate with encapsulated child components usually through props

As input to the component, props should be of the basic js type (string, number, Boolean), but props can pass in more than that. It’s a magic thing. It can pass in things like:

  1. Js basic types (e.g. String, number, Boolean)
<Message text="Hello world!" modal={false} / >;Copy the code
  1. object
<Message
  data={{ 
  thexAxis:  thexAxis ,     
  lineData : lineData
   }} 
  />
Copy the code
  1. An array of
<MoviesList items={['Batman Begins'.'Blade Runner']} / >Copy the code
  1. Can be specified as a function for event handling and asynchronous operations:
<Message type="text" onChange={handleChange} />  
Copy the code
  1. Prop can even be a component constructor. Components can be used to handle instantiations of other components:
function If({ Component, condition }) {  
 return condition ? <Component /> : null;
  }
<If condition={false} component={LazyComponent} />  
Copy the code

To avoid breaking encapsulation, be careful about the details of the props pass. A parent component should also not expose its own structure when it sets up functions to its children. For example, magic operations like passing an entire component instance or refs as props.

Accessing global variables is another issue that negatively impacts encapsulation.

We can use propTypes to type restrict incoming data.

React creates components

React creates components in three ways:

  1. Function-based stateless components
  2. Es5 Use the React. CreateClass component
  3. Es6 extends React.Component

React recommends ES5 and ES6 components, so we won’t discuss ES5 components here.

React.Component

React.createclass creates stateful components in ES6 format. This is the recommended way to create stateful components in React.

React.createclass provides a number of syntactic improvements over the react.createclass method

1.import

ES6 uses import mode instead of ES5 require mode to import modules. Import {} can directly import variable names from modules, which is more concise and intuitive.

2. Initialize state

In ES6 syntax, the React component uses class inheritance to implement it, removing ES5’s getInitialState hook function and declaring state initializations in the constructor constructor.

Extended content:

How do I define State correctly

React treats the component as a state machine. Achieve different states by interacting with the user, and then render the UI to keep the user interface and data consistent. Any UI changes to components can be reflected in changes in State; All states in State are used to reflect UI changes, and there should be no more states.

What variables should be used as component State:

  1. Variables that can be obtained from the parent component through props should not be used as component states.
  2. This variable should not be considered component State if it remains constant throughout the life of the component.
  3. Variables computed from other states (states) or properties (Props) should not be considered component states.
  4. Variables not used in the render method of a component are not used for UI rendering and should not be used as the State of the component. In this case, the variable is better defined as a general property of the component.

Function and class create component differences

React internally obtains rendered nodes by invoking component definitions. The procedure for obtaining nodes varies according to different component definitions. As follows:

//functionWay to definefunction Example() {
  return<div>this is a div</div>; } const node = Example(props); // class Example extends React.Component {render() {
    return <div>this is a div</div>;
  }
}

const instance = new Example(props);
const node = instance.render();
Copy the code

In this case, the function is called directly, and the class needs to instantiate before calling the Render method on the instantiated object.

An error is reported if the class is called as a normal function

Component and PureComponent

Because this aspect has not been understood in detail, so it is only a superficial summary of the difference:

PureComponent is basically the same as Component, except that it provides a shouldComponentUpdate method with shallow comparisons. When the component is updated, if the props and state of the component are not changed, the render method will not trigger, saving the Virtual DOM generation and comparison process to improve performance.

If we want to replace Component with PureComponent, we don’t need to do much, just change Component to PureComponent. However, we can’t replace Component with PureComponent everywhere. It’s up to the actual situation, because we don’t need to go into details here.

Stateful components and stateless components

The stateless component is more of a template definition, receiving props from the parent component and stuffing the props inside the template with an expression {props. XXX}. Stateless components should keep templates pure for reuse, so UI components should generally be stateless. Similar to:

    var Header = (props) = (
        <div>{props.xxx}</div>
   );
Copy the code

Stateful components are typically used to handle defining interaction logic and business data (using expressions like {this.state. XXX} to mount the business data to instances of container components (stateful components can also be called container components, stateless components can also be called presentation components), and then pass props to the presentation components. The presentation component receives the props and plugs the props into the template. Similar to:

class Home extends React.Component {
  constructor(props) {
      super(props);
      };
   render() {
      return (
         <Header/> 
      )
   }
}
Copy the code

8. Higher-order components

Higher-order components feel to me similar to higher-order functions, which take an input of something and then add new features to that input as a new output of something that looks similar to the implementation of the decorator pattern. But since we haven’t written higher-order components so far, we won’t discuss them here.