To read more articles in this series please visit myMaking a blog, this article sample code please visithere.
preface
During development, we often encounter situations where the existing component library cannot meet the requirements and we need to design and implement components ourselves. So how do you design a component that meets product requirements and is easy for developers to use? This paper takes the design of a cascading component as an example to discuss how to design a high-quality component.
Prepare before you start
- If you want to see a Demo of this article, click here.
- If you want to see the source code, click here.
- The source code for this article is written in TypeScript. If you’re not familiar with TypeScript, check out the TypeScript website
- The cascading component in this Demo was developed based on Ant Design, so if you don’t know about it, check out the component overview.
Next, I’ll go through the design and implementation of the components in detail.
Component design
Demand analysis
The reason for developing this component came from a requirement I encountered at work.
The business requirement for the product was to implement a cascade selection for a region, and at the designer’s request, Ant Design’s Cascader cascade selection could not be used, so you needed to implement a cascade selection yourself.
Requirements are typically analyzed from four perspectives:
- Business requirements Perspective
- UI Design Perspective
- Developer perspective
- Coordinate angles with other components
Business requirements Perspective
After we get a requirement, we should not directly think about how to implement it, but first think about whether the requirement is really a requirement. In other words, is there a more basic and core requirement behind this requirement?
Now that we have the requirements for a cascading selection of regions, should we just implement cascading selection of regions? I don’t think so. What we have today is a demand for regional choices, which could be a career choice tomorrow.
Therefore, what we really need to implement is a cascading selection box that supports N levels.
UI Design Perspective
Now that I’ve decided to implement n-level cascading selection boxes, I need to consider their compatibility across screen sizes, so I’ve introduced a Grid Grid into the component. By default, the 3-level cascade style is set, and corresponding configuration options are provided for users as follows:
```
<Cascade
rowProps={{
gutter: 10,
}}
colProps={{
xs: 24,
sm: 24,
md: 8,
lg: 8,
xl: 8,
}}
/>
```
Copy the code
Developer perspective
The components we develop will not only be used by our team members, but will even be open sourced to other developers. It’s important to give developers a better experience.
In order to save developers’ learning costs, we can consider the following ideas:
- Provide as few apis as possible while meeting basic requirements.
- Provide as few configuration items as possible for API options.
- Provide the best possible documentation or comments for the code, especially the API.
The following is an example of the Cascade Props:
``` interface Props<T> { cascadeKeys? : CascadeKeys; // Define the value label children field in dataSource. : T[]; // Specify the currently selected entry onChange? : (value: T[], level: number) => void; // When the option is selected, call this function rowProps? : RowProps; / / line arrangement, may refer to https://ant.design/components/grid-cn/ colProps? : colProps; // Column loading? : boolean[]; // Select the box to load the dataSource? : T[] | CascadeData<T> | T[][]; // Optional data source} ' 'Copy the code
As you can see, only the dataSource in Props is required. That is, if you don’t know anything about configuration items, the component will work just fine with the simplest configuration, such as:
<Cascade
dataSource={pcaCascadeData}
/>
Copy the code
-
When working with TypeScript, type matching is a special consideration. For example, you can pass in a type when using a component, and TypeScript checks for other types used in the onChange event, such as PCAItem in the following example.
<Cascade<PCAItem> dataSource={pcaCascadeData} cascadeKeys={pcaCascadeKeys} onChange={async (value: PCAItem[], level: number) => { setPCAData(value); setPCAIndex(level); }} / >Copy the code
-
For cascaded components, we also need to consider that there are two possible data sources for the dataSource.
-
When the component is initialized, all cascaded data is passed in, such as provincial/local/county/township level data. Corresponds to the Demo of the “synchronous cascade data”, as well as the Props in the dataSource type definition of CascadeData < T > | T [] [].
- In some scenarios, although no cascaded selection box exists, tree data needs to be processed, including data query and validation functions, so this method is encapsulated in
CascadeData
In the class. - Considering that the amount of data in a tree can be very large, it would be inefficient to search the tree for each selection. Therefore, it is designed to directly traverse all nodes in the tree when the component is created, and then store the data of all nodes in each level in the corresponding
Map
, then you can easily query the data. - Although it is time consuming to traverse all nodes during component initialization, I consider this problem to be negligible given the time lag between the user entering the page and the user operating on the component.
- With that in mind, you are
Props
中dataSource
TypeCascadeData<T>
, which means that one was passed in directlynew CascadeData(treeData)
. whileT[][]
Means that the tree data is passed in directly from within the componentnew CascadeData(treeData)
。
- In some scenarios, although no cascaded selection box exists, tree data needs to be processed, including data query and validation functions, so this method is encapsulated in
-
When the component is initialized, only the options at the first level are passed in, and the options at each subsequent level are retrieved from the server using the parameters selected at the previous level. Corresponding to Asynchronous cascading data in Demo, corresponding to T[] in dataSource type.
- When a component initializes, only the first level of data is passed in, and the other levels are passed in empty arrays, as in:
[[{"code":110000,"name":" Beijing "}],[],[]]
, the component will render 3 levels of linkage options. - When making a selection, the user is required to pass
onChange
The event automatically updates the data at the next level. In other words, the component completely relinquishes control of the data.
- When a component initializes, only the first level of data is passed in, and the other levels are passed in empty arrays, as in:
-
Coordinate angles with other components
Because this component needs to work with other Ant Design components, such as the UI section discussed earlier, it incorporates the Grid Grid component. It not only ensures the normal display of the component in all screen widths, but also ensures that the display is consistent with other components.
In addition, you need to consider the compatibility with the Form component, especially the compatibility with Form validation.
conclusion
This paper discusses how to design an excellent component from four angles through a design case of a cascading component. The four angles are:
- Business requirements Perspective
- UI Design Perspective
- Developer perspective
- Coordinate angles with other components
I think a lot of times there is no optimal solution for component design, and it is always a choice between different solutions based on requirements. But you can design a good component by following the four perspectives mentioned in this article.