telerik / kendo-react

Issue tracker - KendoReact http://www.telerik.com/kendo-react-ui/
https://kendo-react-teal.vercel.app
Other
212 stars 37 forks source link

react multisesect triggers onChange event twice when custom itemRender contains checkbox #125

Closed marthrusk closed 5 years ago

marthrusk commented 5 years ago

Multiselect has some problem with triggering onChange event when custom ItemRendering is enabled and contains input, for example checkbox. onChange event is triggered twice in 2.3.2 version. In version 1.2.0 it worked fine without any problem.

Follow this sample and then just copy paste my code below Kendo version: 2.3.2

Edit: solution to this is that onClick + onChange event on input must have this onChange={(e) => { e.stopPropagation(); e.preventDefault() }} onClick={(e) => { e.stopPropagation(); e.preventDefault() }}

https://www.telerik.com/kendo-react-ui/components/dropdowns/multiselect/custom-rendering/

import React from 'react';
import ReactDOM from 'react-dom';
import { MultiSelect } from '@progress/kendo-react-dropdowns';
const sports = [
    { text: 'Basketball', id: 1 },
    { text: 'Football', id: 2 },
    { text: 'Tennis', id: 3 },
    { text: 'Volleyball', id: 4 }
];

class AppComponent extends React.Component {
state = { value: [] };

isCustom(item) { return item.id === undefined; }
addKey(item) { item.id = new Date().getTime(); }

handleChange = (event) => {
    const values = event.target.value;
    const lastItem = values[values.length - 1];
    console.log("triggered");
    if (lastItem && this.isCustom(lastItem)) {
        values.pop();
        const sameItem = values.find(v => v.text === lastItem.text);
        if (sameItem === undefined) {
            this.addKey(lastItem);
            values.push(lastItem);
        }
    }

    this.setState({
        value: values
    });
}
itemRender = (li, itemProps) => {
    const index = itemProps.index;
    const itemChildren = 
    <label>
                <input
          type="checkbox"
                    id={index.toString()}
                    className="k-checkbox"
                    checked={false}/>
                  <label className="k-checkbox-label" htmlFor={index.toString()}></label>
            <span className="k-checkbox-text normal">{li.props.children}</span>
  </label>;

    return React.cloneElement(li, li.props, itemChildren);
}

render() {
    return (
        <div>
            <div className="example-config">
                Selected Values: {JSON.stringify(this.state.value)}
            </div>
            <MultiSelect
                data={sports}
                onChange={this.handleChange}
                value={this.state.value}
                textField="text"
                itemRender={this.itemRender}
                dataItemKey="value"
                allowCustom={true}
            />
        </div>
    );
}
}

ReactDOM.render(
    <AppComponent />,
    document.querySelector('my-app')
);
Xizario commented 5 years ago

Happens because it listens on Click and it is triggered twice. One for the span and one for the outside label. You could alter the behavior by conditionally call the onClick handler:

    return React.cloneElement(li, {
      ...li.props,
      onClick: (e) => (e.currentTarget === e.target) && li.props.onClick(e)
    }, itemChildren);

It would depend on what is the target behavior to be achieved and kind of the template inside.