The front-end framework of this article uses RAX, and the entire code is not open source (to be opened)

preface

How do you design a React /vue component? This article is not a theoretical film, more is my step by step thinking and practice. This article will have a lot of the author’s thinking process, welcome to the comments section more exchanges and discussion.

From requirements discussion, technical solution discussion to coding, to the final test, I experienced many brain bursts and encountered many pits, some of which may be related to the business, some may be related to the framework. Based on these pits, I discussed many solutions and very hack countermeasures. However, as time goes by, looking back at hack code at that time, many people don’t quite remember why they wrote this, so here is a brief record of the development process of Filter component. In order to later query, more hope that we can discuss together, in order to obtain better code architecture and implementation ideas.

Since the code is written using the raX framework based on the underlying WEEX, there are some pitfalls that you may not encounter if you are using React or Vue and can simply ignore

Tell me about the business

A Filter, the most common component, is, as the name suggests, a Filter. Let’s start by looking at some filter representations on existing apps. To make a component, we need it to be universal enough and easy enough to extend.

  • Ali auction Filter

  • Flying pigs in the Filter

Before we talk about the business characteristics of Filter, let’s constrain the naming of each section to make it easier for you to read:

Above are the filter pages of auction and flying pig respectively. From these two pages, we can probably summarize the following business portraits about Filter:

  • As the page scrolls, the Filter may be adsorbed, but it may be at some distance from the top
  • Panel Diversity (Click navItem to expand the Panel)
  • Panel and navItem can also be animated
  • NavBar content is mutable
  • The display form of panel varies
  • Panel Panel content can be very complex and needs to be considered for performance optimization
  • There may be non-filter content on navBar (focus on buttons)
  • Some navBar navItems have no corresponding Panel
  • There is a “fast-sort” button on Filter that affects search results but does not
  • The filter configuration parameter can be specified
  • The panel selection can be initialized by passing the relevant filter ID through the URL
  • .

Final component output

Because the raX 1.0TS +hooks open source version is still under development, the repository link will not be put for now

  • Rax-pui-filter-utils: Internal tool library of filter, provided only by filter developers
  • Rax-pui-filter-tools: Use some tool sets of filter, such as HOC component and placeholder component to improve performance (available or not, according to their own business needs), thinking reasons: Not every Filter user needs these features to be pluggable in order to reduce unnecessary bundle size
  • Pui-filter: filter core function development library

Effect:

The query parameters thrown are visible on the console

Design and Thinking

Front-end Component Architecture Diagram (first version)

Component Architecture Diagram (endboard)

├─ navBar ├─ navBase.js // SRC ├─ file.js //Filter Out the parent container Constant.js // item code constants Definition ├─ index.js // import file ├─ navBar // Navbar folder │ ├─ navBase.js //navBar base class NavQuickSearch and NavRelatePanel parent class │ ├─ NavQuickSearch.js //navBar base class │ ├─ NavRelatepanel.js // ├─ ├─ ├─ style.js // ├─ ├─ style.js // navBar │ ├─ ├─ style.js // navBar │ ├─ ├─ style.js // navBar │ ├─ ├─ style.js // navBar │ ├─ ├─ style.jsCopy the code

Component features

  • The Filter header UI is dynamically configurable and extensible, supports click animation, and provides three filter item types
    • RelatePanel:Filter items associated with Panel type, that is, there is a one-to-one relationship between the filter head and the Panel. Click the filter head to display the Panel
    • QuickSearch:Filter items quickly search sorted type, that is, the filter header does not correspond to the Panel, click the filter header to trigger the search directly
    • PureUI:Pure UI placeholder type, pure UI placement that does not involve searching, such as the subscribe button scenario
  • Screen panel display and hide unified management, support drop down and left slide to show hidden animation, unified search callback function
  • The Filter component is isolated from the service panel and supports the access of any componentonChange(params)The callback function to trigger
  • Three business-common panel components are provided
    • rax-pui-list-selectList to select the business panel
    • rax-pui-location-select, province and region cascade select business panel
    • rax-pui-multi-selection-panel, multi-select business panel,View the component usage documentation

This refers to the functional features of Filter. The functions of Filter components mentioned above may not be completely covered, but we provide solutions. The design of components always adheres to the principle of non-intrusion into business, and all configuration entries related to business are provided.

Expect components to use form

 import Filter from 'rax-pui-filter'; Render (<Filter navConfig={[]} onChange={()=>{}}> < filter. Panel> < business component 1 /> </ filter. Panel> < filter. Panel> < business component 2 /> </Filter.Panel> </Filter> );Copy the code

Boundaries between component functionality and business requirements

What is a business function and what is a component function, this needs to be discussed in detail, in fact, there is no strict distinction. Basically, he gives you a charger when you buy a phone. But… Why do many cell phones come with cases (Xiaomi, Huawei, Honor) but not iPhone? So is it standard?

For our component, in a nutshell: what we can do, we do! But some of the features we tease out are data business features:

  • What copywriting and styles are displayed by each navItem on navBar and belong to business functions
  • The data processing of the whole Filter, including the query parameters on the URL, is also a business function that needs to be thrown to the copy displayed by the corresponding navItem
  • Filter whether to click scroll to the top is also a business function, after all, many search page Filter itself at the top. Also, for RAX, different containers scroll differently (but we provide this method for you to call)
  • Panel Data requests, logic processing are your own business logic. The Filter provides only basic container capabilities and interfaces

In other words, any function in a Filter can be said to be a business function. But we need to provide a Future that encapsulates functionality that 80% of the business needs as a Filter. That’s what we’re trying to do.

Based on the above distinction between business and component functions, we know what configuration and method you should pass to me when using Filter.

Filter API

parameter instructions type Default value (mandatory)
navConfig Filter header configuration,Click to view detailed configuration items



rendering

Array<Object> – (Required)
offsetTop Filter height from the top of the page when the Filter component is expanded. There are two states:A fixed positionandFollow the page scroll adsorption top



A fixed positionHeight from the top of the page in state

Follow page scroll adsorption top:Height from the top of the page in state



rendering

Number 0
styles Configure styles. All styles in Filter are availablestylesCollection object to configure overrides

Styles format

Object {}
getStickyRef Obtain the REF instance of the Sticky node for rolling adsorption scenarios and internal coordinationpm-app-plusContainer components automatically attach to the top when clicking Filter



figure

Function
keepHighlight Whether the filter header needs to remain highlighted after the filter criteria change



rendering

Boolean false
clickMaskClosable Turn on mask background click Hide Boolean true
onChange Filter searches the change callback function

Signature: Function(params:Object,index:Number, urlQuery: Object) => void

Parameters:

params: ObjectThe search parameters

index:NumberTrigger the search Panel search

urlQuery:ObjectURL query object

Function
onPanelVisibleChange Panel shows the hidden callback function

Signature: Function({ visible:Boolean, triggerIndex:Number, triggerType:String }) => void

Parameters:

visible:BooleanDisplay hidden flags

triggerIndex:NumberIndex value of the triggered filter

triggerType:StringTrigger type





TriggerType,There are three trigger types

Navbar: Click trigger from filter header

Mask: Click trigger from the background layer

Panel: The onChange callback from the Panel is triggered
Function

Filter Prop navConfig Array configuration details

navConfig

Filter item type type

  • RelatePanel:Filter items associated with Panel type, that is, there is a one-to-one relationship between the filter head and the Panel. Click the filter head to display the Panel
  • QuickSearch:Filter items quickly search sorted type, that is, the filter header does not correspond to the Panel, click the filter header to trigger the search directly
  • PureUI:Pure UI placeholder type, pure UI placement that does not involve searching, such as the subscribe button scenario

Note that if navConfig’s built-in UI parameters do not meet your requirements, use the renderItem custom rendering function to control the filter header UI

parameter instructions type Default value (mandatory)
type Filter item type



Three types of

RelatePanel: Filter item associated data panel type

QuickSearch: Filter quick search sort type

PureUI: Pure UI placeholder type
String ‘RelatePanel’
text





Pay attention to RelatePanelType to take effect
The filter header displays the copy

Text overflow.show
String – (Required)
icons





Pay attention to RelatePanelType to take effect
Filter header icon: Normal and active ICONS

The data format

ObjectType:


StringType:




rendering

Object or String
options





Pay attention to QuickSearchType to take effect
Quickly search sorted data sources

The data format

Array (required)
optionsIndex





Pay attention to QuickSearchType to take effect
Quick search sort type default selected index String 0
optionsKey





Pay attention to QuickSearchType to take effect
Specifies the search key for quick search sort, used in the onChange callback String Do not provide an index that defaults to the current filter
formatText Text formatting function

Signature:Function(text:String) => text

Parameters:

text: StringFilter head copywriting
Function (text)=>text
disabled Disables filter header clicking Boolean true
hasSeperator Whether to display the right delimiter



rendering

Boolean false
hasPanel Whether the current filter header has a corresponding panel Boolean true
renderItem Custom rendering

Pay attention to

Use when the configuration items provided do not meet your UI requirements

Signature:Function(isActive:Boolean, this:Element) => Element

Parameters:

isActive:BooleanFilter whether the header is active

this:ElementFilter header this instance
Function
animation Animation configuration, using built-in animation

Parameters that


Pay attention toOnly one is currently built inrotateAnimation types

Object
animationHook User – defined animation hook function, used when the built-in animation does not meet the requirements

Signature:Function(refImg:Element, isActive:Boolean) => text

Parameters:

refImg:ElementFilter the REF instance of the header icon

isActive:BooleanFilter whether the header is active
Function

Filter.Panel API

parameter instructions type Default value (mandatory)
styles Configuration style

All styles in Filter are availablestylesCollection object to configure overrides
Object {}
displayMode Panel display format: full-screen, drop-down

Parameters that

Full screen:Fullscreen

Drop down:Dropdown
String ‘Dropdown’
noAnimation Ban on animation Boolean true
highPerformance Internal control Panel through the display hide Panel render times, to avoid unnecessary render, high performance mode, only when the Panel display or display hidden state changes will be rerender Boolean true
animation Panel displays animation configuration, built-in up, down, left and right animation

Parameters that


directionControl animation direction, respectivelyup,down,left,right

Object

Filter code use

  • Filter parameter configuration
  navConfig: [
        {
          type: 'RelatePanel', / /typeThis parameter is optional. The default value is'RelatePanel'
          text: 'down', // Configure the filter header copy ICONS: {// Configure ICONS, divided into the normal form and click the selected form normal:'//gw.alicdn.com/tfs/TB1a7BSeY9YBuNjy0FgXXcxcXXa-27-30.png',
            active: '//gw.alicdn.com/tfs/TB1NDpme9CWBuNjy0FhXXb6EVXa-27-30.png',
          },
          hasSeperator: true// Display the vertical separator formatText: text => text +'left', // Filter text formatting function}, {type: 'QuickSearch',
          optionsIndex: 0,
          optionsKey: 'price', options: [// quicksort list {text:'price',
              icon: ' ',
              value: '0',
            },
            {
              text: 'ascending',
              icon: '//gw.alicdn.com/tfs/TB1PuVHXeL2gK0jSZFmXXc7iXXa-20-20.png',
              value: '1',
            },
            {
              text: 'descending',
              icon: '//gw.alicdn.com/tfs/TB1a7BSeY9YBuNjy0FgXXcxcXXa-27-30.png',
              value: '2',},],}, {type: 'RelatePanel', / /typeThis parameter is optional. The default value is'RelatePanel'
          text: 'rotation'// ICONS: {// set ICONS into the normal form and click on the selected form normal:'//gw.alicdn.com/tfs/TB1PuVHXeL2gK0jSZFmXXc7iXXa-20-20.png',
            active: '//gw.alicdn.com/tfs/TB1l4lIXhv1gK0jSZFFXXb0sXXa-20-20.png',
          },
          animation: { type: 'rotate'}, // Configure animation after click rotate image, default no animation}, {type: 'RelatePanel', / /typeThis parameter is optional. The default value is'RelatePanel'
          text: 'the left'}, {type: 'PureUI',
          text: 'subscribe'RenderItem: () => {// Render custom UIreturn (
              <Image
                style={{
                  width: 120,
                  height: 92,
                }}
                source={{ uri: 'https://gw.alicdn.com/tfs/TB1eubQakL0gK0jSZFAXXcA9pXa-60-45.png'}} / >); },},] // <Filter offsetTop={100} // offsetTop= RecycleView The current value is 100 navConfig={this.state.navConfig} // Filter Navbar configuration item keepHighlight={true} // Keep the changes highlighted styles={styles} // Config override the built-in styles, Big style object collection onChange = {this. HandleSearchChange} / / Panel Panel shows the change event onPanelVisibleChange = {this. HandlePanelVisibleChange} > <Panel highPerformance={true}> <ListSelect {... this.state.data1} /> </Panel> <Panel> <LocationSelect {... this.state.data2} /> </Panel> <Panel displayMode={'Fullscreen'} animation={{// Animation ={// timingFunction:'cubic - the bezier (0.22, 0.61, 0.36, 1)',
                  duration: 200,
                  direction: 'left'}}> <MultiSelect {... this.state.data3} /> </Panel> </Filter>Copy the code

Code operation effect diagram as shown in the screenshot above. Now, a brief description of the code implementation.

Core source code Display

The open source version (Ts+hooks+ Lerna) is not yet available, so the code is still being written in rax 0.x. This is only done where there are holes in the code processing explanation. Welcome to comment and leave your thoughts

Filter.js

Let’s start with the Render method

  render() {
    const { style = {}, styles = {}, navConfig, keepHighlight } = this.props;
    const { windowHeight, activeIndex } = this.state;
    if(! windowHeight)return null;

    return (
      <View style={[defaultStyle.container, styles.container, style]}>
        {this.renderPanels()}
        <Navbar
          ref={r => {
            this.refNavbar = r;
          }}
          navConfig={navConfig}
          styles={styles}
          keepHighlight={keepHighlight}
          activeIndex={activeIndex}
          onNavbarPress={this.handleNavbarPress}
          onChange={this.handleSearchChange}
        />
      </View>
    );
  }
Copy the code

Get some basic configuration, as well as the windowHeight (screen height) and activeIndex (which item is currently active (clicked)).

RenderPanels are written on NavBar because in WEEx zIndex is disabled. To render A above B, A must come after B. This is written for the drop-down drawing of the panel, which appears to come out from under the navBar.

The renderPanel method renders the corresponding panel

/** * Render Panel */ renderPanels = () => {const {activeIndex, windowHeight} = this.state;let { children } = this.props;

    if(! Array.isArray(children)) { children = [children]; }let index = 0;
    return children.map(child => {
      let panelChild = null;
      let hasPanel = this.panelIndexes[index];
      if(! hasPanel) { index++; }if(! this.panelManager[index]) { this.panelManager[index] = {}; }let injectProps = {
        index,
        visible: activeIndex === index,
        windowHeight,
        filterBarHeight: this.filterBarHeight,
        maxHeight: this.filterPanelMaxHeight,
        shouldInitialRender: this.panelManager[index].shouldInitialRender,
        onChange: this.handleSearchChange.bind(this, index),
        onNavTextChange: this.handleNavTextChange.bind(this, index),
        onHidePanel: this.setPanelVisible.bind(this, false, index),
        onMaskClick: this.handleMaskClick,
        disableNavbarClick: this.disableNavbarClick,
      };
      if(child.type ! == Panel) { panelChild = <Panel {... injectProps}>{child}</Panel>; }else {
        panelChild = cloneElement(child, injectProps);
      }
      index++;
      return panelChild;
    });
  };
Copy the code

To be precise, this is HOC, and we pass to the Panel the props that the agent, the translator, or the Filter needs to use. Such as onChange callbacks, or panel hiding callbacks, and which panel needs to be expanded.

Due to the Panel complexity we do not know. In order to avoid continuous expansion and collection of unnecessary render, we adopt transform to remove the panels that do not need to be displayed from the screen, and move the panels that need to be displayed to the inside of the screen. See the render return of Panel

  return (
      <View
        ref={r => {
          this.refPanelContainer = r;
        }}
        style={[
          defaultStyle.panel,
          styles.panel,
          this.panelContainerStyle,
          {
            transform: `translateX(-${this.containerTransformDes})`,
            opacity: 0,
          },
        ]}>
        <View
          ref="mask"
          style={[
            defaultStyle.mask,
            styles.mask,
            showStyle,
            isWeb ? { top: 0, zIndex: -1 } : { top: 0 },
          ]}
          onClick={this.handleMaskClick}
          onTouchMove={this.handleMaskTouchMove}
        />
        {cloneElement(child, injectProps)}
      </View>
    );
Copy the code

For example, we all know that render is the most wasteful of the page performance, and when the page is initialized, the name of the Panel is not displayed (now the Panel is outside the screen), so do we need to render the Panel? However, in the current way of writing, the life cycle of the Panel component will go all the way. However, if the Panel needs to request data, and the query parameter locationId=123 in the URL of the page, navItem needs to display the corresponding geographical location. If the Panel is not rendered, how to get the name of the Panel according to the ID and pass it to navItem to display? Yes, we can intercept the Render method of the Panel, let the Panel render null, and the rest of the life cycle will run as usual. However, if a user uses ref in render, it can cause bugs that are hard to troubleshoot.

So finally, in order to increase the interactivity rate of the page without affecting the page requirements, we provided an optional tool: Performance HOC. Note that this is optional.

export default function performance(Comp) {
  return class Performance extends Comp {
    static displayName = `Performance(${Comp.displayName}) `;render() {
      const { shouldInitialRender } = this.props.panelAttributes;
      if (shouldInitialRender) {
        return super.render();
      } else {
        return<View />; }}}; }Copy the code

Tell me if it’s the first time to block Render by configuring the Panel’s shouldInitialRender attribute.

Of course, Panel also has many other pits, for example, now Panel in order to repeat render, remove Panel off the screen, then, animation from the top to set the initial animation flash screen how to deal with?

The code for Filter is to initialize, format, check and verify various parameters, and communicate between Panel and NavBar, such as format and handleNavbarPress

NavBar core code

NavBar architecture

The core code

Probably can be seen from the architecture diagram, in the NavBar through different configurations, showing different types, NavBarItem NavQuickSearch, NavRelatePanel

Here are some things to note: The NavBar data is passed in through Filter props. If the state is managed by the parent component of the Filter, ShouldComponentUpdate of the Panel layer is provided. Also for high cohesion and low coupling of the component design, we encapsulated the props passed in to the state of the NavBar. Manage your own state.

constructor(props) { super(props); const navConfig = formatNavConfig(props.navConfig); this.state = { navConfig, }; } // We provide the internal formatNavConfig method, which depends on the business requirements of different componentsCopy the code

Another thing to note in NavBar is the passive update: the text on the NavBar is updated when the Panel layer is clicked, because here we use the parent component to communicate between the Panel and NavBar

// filter.js calls NavBar methods /** * Updates NavBar text */ handleNavTextChange = (index, navText, isChange =true) => {// Navbar render is removed to internal processing, Can reduce a Filter. A Panel of the additional render this. AsyncTask (() = > {this. RefNavbar. UpdateOptions (index, navText isChange); }); }; // navbar.js provides updateOptions /** * to the filter. js call to update navConfig, and the Filter component calls * asynchronouslysetState circumvents RAX framework bugs: The user calls the this.props. OnChange callback in componentDidMount to re-code: https://jsplayground.taobao.org/raxplayground/cefec50a-dfe5-4e77-a29a-af2bbfcfcda3
   * @param index
   * @param text
   * @param isChange
   */
  updateOptions = (index, text, isChange = true) = > {setTimeout(() => {
      const { navConfig } = this.state;
      this.setState({
        navConfig: navConfig.map((item, i) => {
          if (index === i) {
            return {
              ...item,
              text,
              isChange,
            };
          }
          returnitem; })}); }, 0); };Copy the code

Finally, there are two kinds of NavBar items: quick search and NavBarItem with panel. However, for its common functions, such as rendering UI logic, the method adopted here is to extract NavBase components. Provides NavQuickSearch and NavRelatePanel calls:

  • NavBase part code
  renderDefaultItem = ({ text, icons, active }) => {
    const { formatText, hasSeperator, length, keepHighlight, isChange } = this.props;

    const hasChange = keepHighlight && isChange;
    const iconWidth = icons ? this.getStyle('navIcon').width || 18 : 0;

    return [
      <Text
        numberOfLines={1}
        style={[
          this.getStyle('navText'),
          ifElse(active || hasChange, this.getStyle('activeNavText')),
          { maxWidth: 750 / length - iconWidth },
        ]}>
        {ifElse(is('Function')(formatText), formatText(text), text)}
      </Text>,
      ifElse(
        icons,
        <Image
          ref={r => {
            this.refImg = r;
          }}
          style={this.getStyle('navIcon')}
          source={{
            uri: ifElse(active || hasChange, icons && icons.active, icons && icons.normal),
          }}
        />,
        null,
      ),
      ifElse(hasSeperator, <View style={this.navSeperatorStyle} />),
    ];
  };
Copy the code
  • NavRelatePanel.js
  export default class NavRelatePanel extends NavBase {
    static displayName = 'NavRelatePanel';
  
    handleClick = () => {
      const { disabled, onNavbarPress } = this.props;
      if (disabled) return false;
      onNavbarPress(NAV_TYPE.RelatePanel);
    };
  
    render() {
      const { renderItem, active, text, icons } = this.props;
  
      return (
        <View
          style={[this.getStyle('navItem'), ifElse(active, this.getStyle('activeNavItem'))]}
          onClick={this.handleClick}>
          {ifElse(
            is('Function')(renderItem), renderItem && renderItem({ active, instance: this }), this.renderDefaultItem({ text, icons, active }), )} </View> ); }}Copy the code

Panel core code

The core function of Panel is to add basic functions to user-defined Panel. Child, such as background mask and animation timing processing.

Use of Panel:

              <Panel
                displayMode={'Fullscreen'} animation={{// Animation ={// timingFunction:'cubic - the bezier (0.22, 0.61, 0.36, 1)',
                  duration: 200,
                  direction: 'left'}}> <MultiSelect {... this.state.data3} /> </Panel>Copy the code

We provide the basic animation configuration, but also the animation functionhooks, depending on when the animation is triggered


  get animationConfig() {
    const { animation } = this.props;
    if(! animation || ! is('Object')(animation)) {
      return PANEL_ANIMATION_CONFIG;
    }
    returnObject.assign({}, PANEL_ANIMATION_CONFIG, animation); } / /... / * * * * to perform animation @ param nextProps * / componentWillReceiveProps (nextProps) {if(nextProps.visible ! == this.props.visible) {if (nextProps.visible) {
        setNativeProps(findDOMNode(this.refPanelContainer), {
          style: {
            transform: `translateX(-${rem2px(750)}) `,}}); this.props.disableNavbarClick(true);
        this.enterAnimate(this.currentChildref, () => {
          this.props.disableNavbarClick(false);
        });
        this.handleMaskAnimate(true);
      } else {
        this.handleMaskAnimate(false);
        this.props.disableNavbarClick(true);
        this.leaveAnimate(this.currentChildref, () => {
          this.props.disableNavbarClick(false);
          setNativeProps(findDOMNode(this.refPanelContainer), {
            style: {
              transform: 'translateX(0)',}}); }); }}}Copy the code

Since animation execution takes time, we should lock NavBar in Filter during this period, and the concept of lock is also provided to users. After all, we will not invade the business logic, and NavBar should be locked when no result is returned in the last search. Disallow re-clicking (although the user can handle this in the onchange callback function, it should also be considered and provided as a component), as well as for animations, which should disallow re-clicking of NavBar while the animation is executing. The animation configuration above looks like this:

There is also the core processing in Panel, which is probably about the processing of animation timing. In front of the trigger animation, for example, we need to set up animation initial state, but if the following wording, there will be a Panel flashing phenomenon, after all, we through the back door of the second event training in rotation to perform initialization, so here, if the user configuration to start the animation, so we need in the outermost layers of the Panel to add a visible flag: Opacity is set to 0 by default. After setting opacity of the outer container to 1, the Panel still flashes, but you can’t see it.

// Set the initial animation stylesetTimeout(() => {
        setNativeProps(node, { style: { transform: ! visible ?'translate(0, 0)': v, }, }); }, 0); // Perform the animationsetTimeout(() => {
        transition(
          node,
          {
            transform: visible ? 'translate(0, 0)' : v,
          },
          {
            timingFunction: timingFunction,
            duration: duration,
            delay: 0,
          },
          cb,
        );
      }, 50);
Copy the code

Set the animation initialization style to add:

        setNativeProps(findDOMNode(this.refPanelContainer), {
          style: {
            opacity: 1,
          },
        });
Copy the code

conclusion

The component of Filter seems simple, but if you want to write a more general and extensive Filter component in the market, not only the granularity, coupling degree and performance of the component need to be considered, but also there are too many business logic need to be considered. For the current initial version (which has not been modified into the official open source version), it has basically covered the current business scenarios we can think of, and relevant businesses have been implemented.

Of course, if it is used directly in business rather than as an open source component, we can lower the level of the Child under the Panel through renderPortal, and manage the data state through EventBus, Redux, Mobx, etc. That will make the whole code logic look a lot clearer. However, in order to reduce the bundle size, we tried to minimize the use of generic packages and dependencies on third-party plug-ins.

Any ideas that are not mentioned in this article or that you have a better way to deal with these Filter business requirements are welcome in the comments section

Technical communication

Welcome to wechat public number: full stack front selection, daily access to high-quality articles push. You can also add my personal wechat communication ~