简体   繁体   中英

How to declare props for react-select in typescript?

Anyone experienced with this case, I don't know how to declare the Select properties in Typescript. Please help! 在此处输入图片说明

import React from 'react'
import Select, {components, IndicatorProps} from 'react-select'
import {connect} from 'react-redux'
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import { Users } from '../../../Redux/Users/UsersReducer';
import { RootState } from '../../../Redux/rootReducer';

interface SelectButtonProps {
  onSelect: (event: React.FormEvent<HTMLSelectElement>) => void,
  users: Users[],
  stateId: string
}
const SelectUserButton:React.FunctionComponent<SelectButtonProps> = ({onSelect, users, stateId}): JSX.Element => {
    const options = users.map( (user:Users) => ({label: user.name, value:`${user.id}`}))

    const DropdownIndicator = (
        props : IndicatorProps<any>
      ) => {
        return (
          <components.DropdownIndicator {...props}>
            <ArrowDropDownIcon id="arrow_icon"/>
          </components.DropdownIndicator>
        );
      };
      const SingleValue = ({ children, ...props }:any) => (
        <components.SingleValue {...props}>
          {children}
        </components.SingleValue>
      );
    return (
        <div className='select__user'>
            <label htmlFor='react-select-container'>Users</label>
            <Select 
                SingleValue={SingleValue}
                name="user"
                components={{ DropdownIndicator }}
                options={options}
                onChange={onSelect}
                maxMenuHeight={120}
                placeholder={stateId}
            />
        </div>
    )
}

I got an error on the onChange event, because when using react-select, it will return an object.

And the browser also shows this error: "
The above error occurred in the <WithStyles(ForwardRef(SvgIcon))> component:
    in WithStyles(ForwardRef(SvgIcon))
    in ForwardRef
    in ForwardRef (at SelectUserButton.tsx:23)
    in div (created by Context.Consumer)
    in EmotionCssPropInternal (created by DropdownIndicator)
    in DropdownIndicator (at SelectUserButton.tsx:22)"

Let's start with the easy errors/issues which are to do with SingleValue .

Your const SingleValue is passing through all of the props it receives so it's not doing anything. You can write const SingleValue = components.SingleValue and it will be the same thing. You can also pass components.SingleValue directly without assigning it to a local variable.

I've had a look at the docs and source code for "react-select" and it doesn't seem like SingleValue is a prop of Select , though oddly this does not trigger a typescript error. Instead it should be an additional property on components alongside DropdownIndicator . It would be components={{ DropdownIndicator, SingleValue: components.SingleValue }} , but since you aren't changing anything from the default, it's not necessary to include SingleValue at all.

Now to the hard part which is the event handling.

Your interface declares that the onSelect prop is a function which receives an event of type React.FormEvent<HTMLSelectElement> . Unfortunately it is not possible to properly call this function because the "react-select" package doesn't pass the actual event to your callback functions.

If you want to keep using this package, you will need to change your SelectButtonProps interface to something which is compatible with the data that you are getting back from the "react-select" onChange function. For example, it can be a function which receives the selected Users object:

interface SelectButtonProps {
  onSelect: (user: Users) => void;
  users: Users[];
  stateId: string;
}

The typescript type of the first argument given to onChange is any , but in practice it is the type of each option in your options array which is {label: string, value: (the type of your user.id which I don't know if it's string or number) } . Frankly, the package author could have done a better job there as they really should be giving you the correct type based on the options array that you are providing. There are multiple places that they should be using generics (value type, option type) where they are using vague types like any instead.

This is a function which you can pass to onChange :

  const handleChange = (option: OptionTypeBase, meta: ActionMeta<any>): void => {
    console.log({ option, meta });
    onSelect({
      name: option.label,
      id: option.value
    });
  };

This is not necessary, but an alternative way of approaching this is that instead of mapping the users to a {label, value} and then mapping that {label, value} option back to a User in your onChange callback, what you can do is pass the users array directly to Select as options . Then instead of using the default behaviors for getOptionLabel ( option => option.label ) and getOptionValue ( option => option.value ), you can pass your own mappings, which are user => user.name and user => user.id .

Revised Code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM