1

I'm working with this 3rd party dropdown - primereact - and it's component primereact/multiselect ... I need everything to work exactly as the original developer wrote, except a few functions. These functions are not accessible via props. I'm wondering if there is a "best practices" way. The problem with the original code -- there is a bug in the "Select All" checkbox, and I want to change the function of the close icon; it is redundant in my application.

These are the only two options I could think of:

  1. Copy entire source code of 3rd party item, modify it directly, and then remove the dependency. This kind of feels like stealing to me, though.
  2. Extend the component and override the functions

Here is the 3rd party source code: https://bit.dev/primefaces/primereact/multiselect/~code#multiselect/MultiSelect.js

and here is my code:

import React from 'react';
import { MultiSelect } from 'primereact/multiselect';
import {customMultiSelectSearchHeader} from './searchSafeMultiSelect.css';

class SearchSafeMultiSelect extends MultiSelect {
    constructor(props) {
        super(props);
        this.state = { 
            filter: ''
        };
        // Binds certain functions to NEW class, instead of __proto__
        this.hasFilter = this.hasFilter.bind(this);
        this.onClick = this.onClick.bind(this);
        this.onOptionClick = this.onOptionClick.bind(this);
        this.onOptionKeyDown = this.onOptionKeyDown.bind(this);
        this.onFocus = this.onFocus.bind(this);
        this.onBlur = this.onBlur.bind(this);
        this.onFilter = this.onFilter.bind(this);
        this.onCloseClick = this.onCloseClick.bind(this);
        this.onToggleAll = this.onToggleAll.bind(this);
    }
    
    // Changes icon next to search bar from X to trash can. 
    originalRenderHeader = this.renderHeader;
    renderHeader = (items) => {
        const header = this.originalRenderHeader(items);
        return (
            <div className={customMultiSelectSearchHeader}>
                {header}
            </div>
        );
    }

    // Makes clicking the Trash (previously X) icon close the dropdown AND clear selection.
    onCloseClick = (event) => {
        this.hide();
        this.updateModel(event.originalEvent, []);
        event.preventDefault();
        event.stopPropagation();
    }

    // Fixes bug in dependency - ALL box is checked if all visible options are selected.
    isAllChecked = (visibleOptions) => {
        if(this.hasFilter()) {
            return this.props.value && visibleOptions && visibleOptions.length 
            && this.filterOptions(this.props.options).find(visible => this.props.value.indexOf(this.getOptionValue(visible) == -1) == undefined;
        }
        else {
            return this.props.value && this.props.options && (this.props.value.length === this.props.options.length);
        }
    }
}


export default SearchSafeMultiSelect;

CSS file ./searchSafeMultiSelect.css:

.customMultiSelectSearchHeader button span::before{
    content: "\e93d" !important;
}

package.json (sets primereact version to exactly 5.0.1 incase they update, my code should stay the same)

    "primereact": "5.0.1",

It works as expected. I am just wondering if I'm doing something taboo over here. I looked up many guides, but didn't see anyone online modifying dependencies this way. That is making me doubt if my approach is safe.

Edit: Yes, I've read many things about "Composition over Inheritance" here, but in the case of 3rd party components, the only way I can change the base's composition is by altering the dependency's source code directly. So, I feel like I'm stuck between 2 bad options -- changing the source code or using inheritance.

Clay
  • 64
  • 1
  • 6
  • 1
    If it truly is a bug: 1) file [an issue](https://github.com/primefaces/primereact/issues) 2) [fork the project](https://docs.github.com/en/github/collaborating-with-pull-requests/working-with-forks/about-forks) 3) fix the bug in your fork 4) change your dependency to use your fork 5) submit a [pull request](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) asking them to merge your changes into the project. [https://github.com/primefaces/primereact/](https://github.com/primefaces/primereact/) – ray Sep 22 '21 at 16:19
  • One of the issues is a bug, but there is a second modification which is project specific. I'll try to push the big fixed in primereact itself, but I still have the dilemma with the second modification. – Clay Sep 22 '21 at 16:43
  • 1
    You could do the second mod in another fork. I'm not aware of any technical reason you can't extend the component, but it's a bit of a [code smell](https://en.wikipedia.org/wiki/Code_smell) and I'd think carefully before heading down that path. The React team even [advises against it](https://reactjs.org/docs/composition-vs-inheritance.html#so-what-about-inheritance). (I get that this is complicated by the fact that the base component is third-party, but still.) – ray Sep 22 '21 at 17:04
  • Yes, that's a good point. Turns out the bugs even got fixed in an update, so I closed the issue. Oops! Unfortunately, the fixed version isn't compatible with other elements in my app. So what I'll do -- if I am able to post a fork to my organization's Git system, I'll use a custom package. Otherwise, I'll copy/edit the component needed.. Copying the component makes more sense now, seeing the version we need is outdated. I'd rather have an in-app copy to ensure nobody in the future tries to update. I'd link the original in the code comments tho! Thanks for the tips. – Clay Sep 22 '21 at 21:09

0 Answers0