Desiigner
Desiigner

Reputation: 2316

Typescript React <Select> onChange handler type error

I'm trying to add an onChange event handler to the Select component from material-ui:

<Select
      labelId="demo-simple-select-label"
      id="demo-simple-select"
      value={values.country}
      onChange={handleCountryChange}
    >
      {countries.map(c => {
        return (
          <MenuItem value={c}>{c}</MenuItem>
        )
      })}
    </Select>

and my event handler:

const handleCountryChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    setValues({...values, country: event.target.value});
  };

but I get the following error:

Type '(event: ChangeEvent) => void' is not assignable to type '(event: ChangeEvent<{ name?: string | undefined; value: unknown; }>, child: ReactNode) => void'.

What's wrong?

Upvotes: 43

Views: 64377

Answers (9)

Nikolay Ponomarenko
Nikolay Ponomarenko

Reputation: 9

I also encountered such an issue in my app. Here is how I solved it (I provide you the whole example):

import { useState } from 'react'
import SelectState from './components/SelectState';
import { SelectChangeEvent } from '@mui/material';

const App = () => {
  const [state, setState] = useState('');
  
  const handleStateChange = (event: SelectChangeEvent) => {
    setState(event.target.value as string);
  }

  return (
    <div className='bg-[#AB92BF] w-[100wh] h-[100vh] p-4'>
      <SelectState state={state} onSelectChange={(event: SelectChangeEvent) => handleStateChange(event)} />
    </div>
  )
}

export default App;



import { Box, FormControl, InputLabel, MenuItem, Select } from '@mui/material'
import { SelectStateProps } from '../interface'

const SelectState = ({ state, onSelectChange }: SelectStateProps) => {
  return (
    <Box sx={{ minWidth: 220 }}>
      <FormControl fullWidth>
        <InputLabel id="state-select-label">State</InputLabel>
        <Select
          labelId="state-select-label"
          id="state-select"
          value={state}
          label="Age"
          onChange={onSelectChange}
        >
          <MenuItem value={"America/Los_Angeles"}>Los Angeles</MenuItem>
          <MenuItem value={"America/New_York"}>New York</MenuItem>
          <MenuItem value={"America/Detroit"}>Detroit</MenuItem>
        </Select>
      </FormControl>
    </Box>
  )
}

export default SelectState



import { SelectChangeEvent } from "@mui/material";

export interface SelectStateProps {
  state: string;
  onSelectChange: (event: SelectChangeEvent) => void;
}

Upvotes: 0

msehli mohamed
msehli mohamed

Reputation: 21

im using import Select from '@mui/joy/Select'; import Option, { optionClasses } from '@mui/joy/Option';

all the solutions from the above didn't work

this did work

const handleChange = (event: React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent | React.FocusEvent<Element, Element> | null, value: any) => { setSort(value); };

Upvotes: 2

Jordan Salinas
Jordan Salinas

Reputation: 196

None of these are correct IMO. Casting "as" should generally be avoided since you'll lose the type checking you get from letting the code infer the type. It's also weird to explicitly type it as unknown only to later cast it to a string. Ideally you'd use a typeguard if you truly didn't know. To be clear, if somehow event.target.value is actually a boolean and we're casting it to a string here we won't know until the app throws a bug later down the line.

The docs (https://mui.com/material-ui/api/select/) have this as the onChange signature:

function(event: SelectChangeEvent<T>, child?: object) => void

event: The event source of the callback. You can pull out the new value by accessing event.target.value (any). 

Warning: This is a generic event not a change event unless the change event is caused by browser autofill.

child: The react element that was selected when native is false (default).

So with that in mind it becomes:

const handleCountryChange = (event: SelectChangeEvent) => {
  setValues({...values, country: event.target.value });
};

We don't need to define T here explicitly as a string because it's a string by default. HTML inputs read in as strings.

Upvotes: 12

Freddy Castano
Freddy Castano

Reputation: 67

Had that issue while using Material UI. Didn't know they had their own types

import { SelectChangeEvent } from '@mui/material/Select';

const changeSymbol = (event: SelectChangeEvent) => {
        console.log(event)
}

Upvotes: 5

froopydoop
froopydoop

Reputation: 95

I don't know where it's documented, but the Select module also exports SelectChangeEvent. This is what I used to get it:

import Select, {SelectChangeEvent} from '@mui/material/Select';

    const handleGenerationSelectChange = (event: SelectChangeEvent<string>, child: React.ReactNode) => {
    let genHolder = currentGeneration;
    genHolder.source = event.target.value as string;
    setCurrentGeneration({...genHolder});
};

Upvotes: 0

Alex
Alex

Reputation: 433

Seemed counterintuitive for me either, but I had to do this:

const handleChange = (e: ChangeEvent<{ value: string | unknown }>) => formik.setFieldValue('field', e.target.value)

Upvotes: 1

httpete
httpete

Reputation: 2983

This is proper for the latest MUI 5 / Select

  import { SelectChangeEvent } from "@mui/material";

   const handleOnChange = (event: SelectChangeEvent<unknown>) => {
    const value = event.target.value as YourEnumType;
  };

Upvotes: 29

Asaf Aviv
Asaf Aviv

Reputation: 11770

Since MUI Select in not a real select element you will need to cast e.target.value using as Type and type the handler as React.ChangeEvent<{ value: unknown }>

const handleCountryChange = (event: React.ChangeEvent<{ value: unknown }>) => {
  setValues({...values, country: event.target.value as string});
};

Upvotes: 66

ssbarbee
ssbarbee

Reputation: 1712

It seems the definition for the change event is incorrect in the SelectInput.d.ts. More here: https://github.com/mui-org/material-ui/issues/15400#issuecomment-484891583

Try to use their recommended signature:

const handleCountryChange = (event: any) => { ... }

Upvotes: -6

Related Questions