Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass additional properties to a custom react-select component?

I am creating a custom MultiValueRemove component for my react-select Select input. I would like to pass additional properties into my custom component; however, I don't see an easy way to do that without going against react-select's rules on inline declaration.

My code currently looks like this (forked from the default MultiValueRemove example):

import React, { useState } from "react";
import EmojiIcon from "@atlaskit/icon/glyph/emoji";
import Select, { components, MultiValueRemoveProps } from "react-select";
import { ColourOption, colourOptions } from "./docs/data";

const CustomMultiValueRemove = (props: MultiValueRemoveProps<ColourOption>) => {
  return (
    <components.MultiValueRemove {...props}>
      <EmojiIcon label="Emoji" primaryColor={colourOptions[2].color} />
    </components.MultiValueRemove>
  );
};

export default () => {
  // I would like to track the number of times the remove icon has been clicked.
  // How can I pass these into my custom MultiValueRemove component?
  const [numberOfRemoveClicks, setNumberOfRemoveClicks] = useState(0);

  return (
    <Select
      closeMenuOnSelect={false}
      components={{ MultiValueRemove: CustomMultiValueRemove }}
      defaultValue={[colourOptions[4], colourOptions[5]]}
      isMulti
      options={colourOptions}
    />
  );
};

How can I pass numberOfRemoveClicks and setNumberOfRemoveClicks to MultiValueRemove?

One potential solution I see is to inline the call to the custom component, pass my custom properties there, and override innerProps as necessary. For example:

interface CustomMultiValueRemoveProps
  extends MultiValueRemoveProps<ColourOption> {
  numberOfRemoveClicks: number;
  setNumberOfRemoveClicks: (value: number) => void;
}

const CustomMultiValueRemove = ({
  numberOfRemoveClicks,
  setNumberOfRemoveClicks,
  innerProps,
  ...props
}: CustomMultiValueRemoveProps) => {
  const newInnerProps = {
    ...innerProps,
    onClick: (event: any) => {
      setNumberOfRemoveClicks(numberOfRemoveClicks + 1);
      innerProps.onClick(event);
    }
  };

  return (
    <components.MultiValueRemove innerProps={newInnerProps} {...props}>
      <EmojiIcon label="Emoji" primaryColor={colourOptions[2].color} />
    </components.MultiValueRemove>
  );
};

export default () => {
  const [numberOfRemoveClicks, setNumberOfRemoveClicks] = useState(0);

  return (
    <Select
      ...
      components={{
        // Pass properties in here
        MultiValueRemove: (props) => {
          return (
            <CustomMultiValueRemove
              numberOfRemoveClicks={numberOfRemoveClicks}
              setNumberOfRemoveClicks={setNumberOfRemoveClicks}
              {...props}
            />
          );
        }
      }}
      ...
    />
  );
};

The above solution works just fine; however, as I mentioned at the top of the question, this goes against react-select's rules on inline declaration so I'd like to avoid this approach if possible.

Here is a link to a code sandbox containing some example code I forked: https://codesandbox.io/s/codesandboxer-example-forked-93dtto?file=/example.tsx

Any advice would be greatly appreciated!

like image 302
Benjamin U. Avatar asked Oct 25 '25 14:10

Benjamin U.


2 Answers

I think you should be able to do something like this

import React, { useState } from "react";
import EmojiIcon from "@atlaskit/icon/glyph/emoji";
import Select, { components, MultiValueRemoveProps } from "react-select";
import { ColourOption, colourOptions } from "./docs/data";

const CustomMultiValueRemove = (props: MultiValueRemoveProps<ColourOption>) => {
  return (
    <components.MultiValueRemove {...props}>
      <EmojiIcon label="Emoji" primaryColor={colourOptions[2].color} />
    </components.MultiValueRemove>
  );
};

export default () => {
  // I would like to track the number of times the remove icon has been clicked.
  // How can I pass these into my custom MultiValueRemove component?
  const [numberOfRemoveClicks, setNumberOfRemoveClicks] = useState(0);

  // you can memoize this if needed 
  const renderCustomMultiValue  = (props) => (
    <CustomMultiValueRemove 
    numberOfRemoveClicks={numberOfRemoveClicks}
    setNumberOfRemoveClicks={setNumberOfRemoveClicks}
    {...props}
    />
  )

  return (
    <Select
      closeMenuOnSelect={false}
      components={{ MultiValueRemove: renderCustomMultiValue }}
      defaultValue={[colourOptions[4], colourOptions[5]]}
      isMulti
      options={colourOptions}
    />
  );
};
like image 176
RodSarhan Avatar answered Oct 27 '25 02:10

RodSarhan


The much more straightforward approach would be to add custom properties to Select and access them in the component via props.selectProps.<customPropertyName>.

Here is the source of the sample code below.

import Select, { components, GroupBase, SingleValueProps } from 'react-select';
declare module 'react-select/dist/declarations/src/Select' {
  export interface Props<
    Option,
    IsMulti extends boolean,
    Group extends GroupBase<Option>
  > {
    myCustomProp: string;
  }
}

function SingleValue({
  children,
  ...props
}: SingleValueProps<MyOption, false>) {
  return (
    <div>
      <label htmlFor={props.selectProps.name}>
        {props.selectProps.myCustomProp}
      </label>
      <components.SingleValue {...props}>{children}</components.SingleValue>
      <div className="ml-1">
        <components.DownChevron />
      </div>
    </div>
  );
}

export default function App() {
  return (
    <Select<MyOption>
      id={id}
      options={options}
      components={{ SingleValue }}
      myCustomProp="foobar"
    />
  );
}
like image 26
nonybrighto Avatar answered Oct 27 '25 04:10

nonybrighto



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!