Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material-ui's Switch component onChange handler is not firing

I've put some Switches in an app and they work fine. Then I put the Switches in another app, but they don't work when clicked.

Both apps are using the same component. Here it is working in one app:

enter image description here

And here's the other app, not working:

enter image description here

In the second app, the onChange handler doesn't seem to ever fire.

The code in both apps looks like the following:

<Switch
  checked={(console.log('checked:', status === 'visible'), status === 'visible')}
  onChange={(e, c) => console.log('event:', e, c)}
/>

In the first app I see the output of those console.logs, while in the second app I only see the initial console.log of the checked prop, but I never see any of the onChange prop.

I checked if any ancestor elements have click handlers, and I didn't find any that are returning false, calling stopPropagation, or calling preventDefault.

Notice in the gif that when I click, the ripple effect still works, so click handling is obviously still working.

Any ideas why onChange may not be firing?

UPDATE! I replaced the switches with regular <input type="checkbox"> elements, and it works great! See:

enter image description here

Looks to me like something is wrong with material-ui's <Switch> component. I have a hunch that I will investigate when I get a chance: there might be more than one React singleton in the application. I'll be back to post an update.

like image 880
trusktr Avatar asked Jan 17 '19 23:01

trusktr


People also ask

What are switches in react material UI?

Material UI is a Material Design library made for React. It’s a set of React components that have Material Design styles. In this article, we’ll look at how to add switches with Material UI. Switches are toggles that let us set something on or off.

Why does it render element with checkbox role not switch?

It will render an element with the checkbox role not switch role since this role isn't widely supported yet. Please test first if assistive technology of your target audience supports this role properly.

What is a muiswitch component?

The name MuiSwitch can be used when providing default props or style overrides in the theme. If true, the component is checked. The icon to display when the component is checked. Override or extend the styles applied to the component.


4 Answers

I think, this is a weird fix and it is working smoothly for me. So, instead of handleChange I am using handleClick. I am not using event here, instead I am passing a string which is obviously the name of the state or id in case of arrays.

<Switch
    checked={this.state.active}
    onClick={() => this.handleToggle('active')}
    value="active"
    inputProps={{ 'aria-label': 'secondary checkbox' }}
/>

handleToggle = (name: string) => {
    this.setState({ active: !this.state.active });
};

I tried handleChange, but the problem still persists. I hope this will get fixed soon.

like image 90
AD B Avatar answered Oct 07 '22 01:10

AD B


I've had the same issue with Checkbox and Switch in the WordPress admin area.

Turns out, there was global CSS rule like:

input[type="checkbox"] {
  height: 1rem;
  width: 1rem;
}

Clicking the upper left corner of the element works, though.

As a solution, I reset some styles in my app root.

EDIT: Nevermind, I just put my whole app into shadow DOM. There are a few gotchas, I'll list them here:

  1. You have to provide a custom insertion point for Material-UI style elements inside the shadow DOM. In general, you have to make nothing gets put outside of you shadow DOM.
  2. You have to load/link the font inside the shadow DOM and outside the shadow DOM (the light DOM).
  3. Use ScopedCssBaseline instead of the global reset.
  4. Dialogs have to have their container prop specified.

This is how I've set things up with Material-UI:

// configure-shadow-dom.js

import { create } from 'jss';
import { jssPreset } from '@material-ui/core/styles';

const shadowHostId = 'my-app-root-id'

export const appRoot = document.createElement('div')
appRoot.setAttribute('id', 'app-root')

const styleInsertionPoint = document.createComment('jss-insertion-point')
export const jss = create({
    ...jssPreset(),
    insertionPoint: styleInsertionPoint,
})

const robotoFontLink = document.createElement('link')
robotoFontLink.setAttribute('rel', 'stylesheet')
robotoFontLink.setAttribute('href', 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap')

const shadowHost = document.getElementById(shadowHostId)
shadowHost.attachShadow({ mode: 'open' })

const shadowRoot = shadowHost.shadowRoot
shadowRoot.appendChild(robotoFontLink)
shadowRoot.appendChild(styleInsertionPoint)
shadowRoot.appendChild(appRoot)


// index.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import ScopedCssBaseline from '@material-ui/core/ScopedCssBaseline';
import { StylesProvider } from '@material-ui/core/styles';
import App from './App';
import { jss, appRoot } from './configure-shadow-dom';

ReactDOM.render(
    <React.StrictMode>
        <StylesProvider jss={jss}>
            <ScopedCssBaseline>
                <App />
            </ScopedCssBaseline>
        </StylesProvider>
    </React.StrictMode>,
    appRoot
);
like image 38
Josef Wittmann Avatar answered Oct 07 '22 00:10

Josef Wittmann


It turns out that in my case, there was a CSS in the page, something like

.some-component { pointer-events: none; }
.some-component * { pointer-events: auto; }

where .some-component was containing my material-ui buttons and switches. I had to manually set pointer-events to all (or some value, I don't remember at at the moment) for the elements inside the switches.

So, that's one thing to look out for: check what pointer-events style is doing.

like image 1
trusktr Avatar answered Oct 07 '22 02:10

trusktr


in Switch component, changing onChange to onClick worked for me:

<Switch
    checked={this.state.active}
    onClick={() => this.handleToggle('active')}
    value="active"
    inputProps={{ 'aria-label': 'secondary checkbox' }}
/>
like image 1
Muho Avatar answered Oct 07 '22 02:10

Muho