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

Viewed 111

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!

1 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}
    />
  );
};
Related