In react-vchart, the encapsulation of all charts is achieved through the higher-order component createChart. Next, we will take the implementation of <VChart /> as an example to explain the implementation principle in detail.
When the component is destroyed, ensure that resources can be released, including:
Release chart instances
Clean up event bindings
Update component status
Implementation of BaseComponent
2.1 Core Implementation Mechanism
In the react-vchart framework, the creation of all components relies on the createComponent factory function. The definition of this function is as follows:
Here, the generic T extends ComponentProps is used to constrain the type of component properties passed in. The function receives multiple parameters:
componentName: Used to identify the name of the component, which is unique throughout the application, making it easy for developers to recognize and manage components.
specName: Represents the specification name corresponding to the component, which is very important for configuration collection and management. Different components are distinguished by different specification names for their respective configurations.
supportedEvents: An optional object used to define the events supported by the component. The key-value pair form of the object represents the event type and the corresponding event handling logic. For example, a component may support the click event and define the corresponding handler function.
isSingle: A boolean value used to indicate whether the component is in singleton mode. If true, it means that there will only be one instance of the component in the entire application; if false, multiple instances can be created.
registers: An array of functions, each used to perform specific registration operations. These registration operations may include registering specific functions or plugins of the component into the framework.
To understand more intuitively, the following shows the encapsulation code using the axis component and legend component as examples:
Here, a coordinate axis component named Axis is created, the component name is Axis, and the corresponding specification name is axes. In this way, the framework can accurately identify and handle the relevant configurations and operations of the coordinate axis component.
This code creates a Legend component, with the component name being Legend and the specification name being legends. It also specifies the custom events supported by this component LEGEND_CUSTOMIZED_EVENTS, and the component is not in singleton mode (false). Finally, a registrar function registerDiscreteLegend is passed in to perform specific registration operations, which may be to register discrete data-related functions for the legend component.
In a React application, communication between components is an important issue. Here, the useContext hook function is used to achieve communication between components. RootChartContext is a context object that contains information related to charts, such as chart instances, global configurations, etc. By using useContext(RootChartContext), components can access this global information to achieve data sharing and interaction between components. For example, a child component may need to obtain the global configuration information of the chart to adjust its display mode, and it can obtain context information in this way.
This part of the code implements the event binding functionality of the component. When the component defines supportedEvents (i.e., supported events), the bindEventsToChart function is called to bind events. This function receives four parameters:
context.chart: Represents the chart instance, obtained through the context. Events are bound to this chart instance so that the handling logic can be triggered when the chart experiences corresponding events.
props: The properties of the component, which may include configuration information related to events, such as event handler functions.
eventsBinded.current: Possibly an object or variable that stores the events that have already been bound, used to record the currently bound events to avoid duplicate bindings.
supportedEvents: The aforementioned object of events supported by the component, containing the mapping relationship between event types and handler functions. In this way, the component can interact with the chart instance to achieve the association between user operations and component behavior.
2.3 Configuration Collection Mechanism
Each component implements the parseSpec method to parse the configuration corresponding to the component, ultimately assembling it into the complete spec required by vchart:
parseSpec method plays a key role in component configuration management. It receives the component's props as a parameter and returns an object containing three attributes:
spec: The component configuration obtained through the pickWithout(props, notSpecKeys) method. The pickWithout function might be a custom function used to filter out the required configuration information from props, excluding unnecessary keys (notSpecKeys). This configuration information will be used as the actual configuration for the component in vchart.
specName: The aforementioned component specification name, used to identify the type of component configuration, facilitating differentiation and management in the overall configuration.
isSingle: A boolean value indicating whether the component is in singleton mode. This information is also crucial in the process of configuration assembly and management, for example, when handling multiple component configurations, it is necessary to decide how to merge configurations based on the value of isSingle. By implementing the parseSpec method for each component, the framework can collect the configuration information of each component and ultimately assemble it into a complete configuration spec that meets vchart requirements.
2.4 Component Registration Mechanism
if (registers && registers.length) {
VChart.useRegisters(registers);
}
This part of the code implements the component registration mechanism. When a component defines registers (i.e., an array of registrars) and the array is not empty, the VChart.useRegisters(registers) method is called. VChart may be a global chart object or a framework core object, and the useRegisters method is used to register functions from the registrar array into the framework. These registrar functions may be used to register specific features, plugins, or integrations with other modules for the component. In this way, the component can register some of its special features or configurations into the framework to function throughout the application.
This code implements the configuration filtering function. The notSpecKeys variable is used to store unwanted configuration keys. If the component defines supportedEvents (i.e., supported events), then notSpecKeys is formed by merging all keys of supportedEvents with ignoreKeys; otherwise, notSpecKeys is directly equal to ignoreKeys. ignoreKeys may be a predefined array containing some keys that need to be ignored during the configuration parsing process. In this way, during the configuration collection and parsing process, unwanted configuration information can be excluded, ensuring that the final configuration spec only contains useful information, thereby improving the accuracy and effectiveness of the configuration.
2.6 Update Control
if (props.updateId!== updateId.current) {
updateId.current = props.updateId;
// 处理更新逻辑...
}
This part of the code implements the update control of the component. updateId is an identifier used to control component updates. When the props.updateId received by the component is not equal to the currently stored updateId.current, it indicates that an update has occurred. At this point, updateId.current is updated to props.updateId, and then the subsequent update logic is executed (indicated in the code by the comment // Handle update logic...). This update control mechanism ensures that when the component receives a new update identifier, it can correctly handle update operations, such as re-rendering the component, updating data, or performing specific update tasks, thereby ensuring that the component's state remains consistent with the latest requirements.
Implementation of BaseSeries
The series components of React-VChart are also mainly implemented using higher-order components. The following will provide a more detailed analysis of its core implementation.
This factory function plays a core role in the creation process of the entire series of components. It strictly constrains the type of component properties passed in through the generic \u003CT extends BaseSeriesProps\u003E, ensuring type safety.
The parameters received by the function have their own important responsibilities:
componentName: Serves as the unique identifier of the component, having uniqueness throughout the application. This makes it more convenient for developers to manage and identify components, just like giving each component a unique "name tag".
markNames: An array of series element names used to determine the elements used by the component.
type: The chart type, although an optional parameter, clarifies the chart type corresponding to the component.
registers: Declares the resources that the series needs to register, used for on-demand loading through tree-shaking to achieve package size optimization.
In the process of chart rendering, each graphic mark needs to have a unique identifier, which is the role of mark ID management. The addMarkId function receives spec (configuration object) and seriesId (series ID) as parameters.
The function generates a default markId for each graphic mark by traversing the markNames array. The generation rule is to concatenate seriesId and markName with -, for example, 'series1-area'.
If a property corresponding to a markName does not exist in spec, an object containing the default markId is created; if the markName property exists but id does not, the default markId is set for it. This ensures that each graphic mark has a unique identifier, facilitating subsequent event handling and style setting operations.
The event handling system is responsible for processing user interactions with the chart. The handleEvent function receives an event object e.
First, generate all possible markId arrays markIds through the markNames array. Then check whether the mark in the event object e exists and whether the user ID of mark is in the markIds array.
If the conditions are met, it indicates that the event is triggered by the graphic mark of the current component. Then, find the corresponding event handler through props[VCHART_TO_REACT_EVENTS[e.event.type]] and pass the event object e into it to perform the corresponding operation. VCHART_TO_REACT_EVENTS is a mapping table used to map VChart's event types to React component's event handlers.
The configuration parsing function Comp.parseSpec is responsible for parsing the component's properties into a configuration object that meets the requirements of VChart.
First, the function pickWithout<T>(compProps, notSpecKeys) is used to filter out the required configuration information from compProps, excluding unnecessary keys notSpecKeys. notSpecKeys may contain some properties unrelated to event handling or other aspects, ensuring the purity of the configuration object in this way.
Then, the addMarkId function is called to add a mark ID to the new series configuration newSeriesSpec, ensuring that each graphic mark has a unique identifier.
Next, if the type parameter is not empty, the chart type is set in newSeriesSpec.
Finally, an object containing spec (the parsed configuration object) and specName (the configuration type name, here as 'series') is returned. This object will be passed to VChart for rendering as the final configuration.
Through the detailed analysis above, we have gained a deeper understanding of the implementation principles of the React-VChart series components, including component creation, property definition, and the implementation of core functions. These technical principles provide a solid foundation for developers when using and extending React-VChart.
This document was revised and organized by the following personnel