44

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?

10 Answers 10

66

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});
};
Sign up to request clarification or add additional context in comments.

2 Comments

Even this is not working. src/core/content/content.component.tsx Line 67:37: Parsing error: Missing semicolon. 65 | 66 | const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { > 67 | const value = event.target.value as string[]; | ^ 68 | debugger; 69 | setDataLines(value); 70 | };
yes its not working
29

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;
  };

5 Comments

This is not working for me. I see SelectChangeEvent defined as export type SelectChangeEvent<T = string> = | (Event & { target: { value: T; name: string } }) | React.ChangeEvent<HTMLInputElement>;
Am I missing something?
I had to do something like this: const handleThingChanged = (event: ChangeEvent<{ name?: string, value: unknown}>): void => {
Something very close to this worked for me. Importing event type definitions from Mui seems like the way to go. const handleChange = (event: SelectChangeEvent<string>) => { onMoodChange(event.target.value); };
Woof, this worked for me, but it's ridiculous that you have to use <unknown> that way. I tried the solution from @AleshHoudek's comment, which at least makes a lot more sense, but that didn't work.
12

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.

2 Comments

This is the best explanation I've ever come across to this problem!
This helped me a lot, thanks! My problem is that the onChange handler was declared as taking an argument of type React.ChangeEvent instead of SelectEvent; I am not sure if this incompatibility was introduced by upgrading TypeScript, React, or MaterialUI, but in any case it works now.
5

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)
}

1 Comment

Best answer! Should be chosen as a default.
1

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

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

Comments

1

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); };

1 Comment

Make sure to format everything correctly, using code formatting, ponctuation and all.
0

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});
};

1 Comment

Isn't using the as string duplicating the string passed to the generic? I guess it's just double the work 🤷🏻‍♂️
0

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;
}

2 Comments

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
I don't think casting with as string is a type safe solution, you're just manually overriding the ambiguity of the types.
0

After looking at the answers above and not being satisfied with them, I discovered you could just type the Select object with the following code:

const [booleanValue, setBooleanValue] = useState<boolean>(false);
const BooleanSelect = Select<boolean>;
const handleBooleanSelect = (event: SelectChangeEvent<boolean>) {
    setBooleanvalue(event.value);
}

...
<FormControl>
<InputLabel>Enable Feature</InputLabel>
<BooleanSelect value={booleanValue} onChange={handleBooleanSelect}>
  <MenuItem value="true">Enabled</MenuItem>
  <MenuItem value="false">Disabled</MenuItem>
</BooleanSelect>
</FormControl>

Comments

-6

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) => { ... }

1 Comment

this is not their recommended signature, that's a lazy any ahahah

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.