I have a container component where I have few functions, like this two for example:
newPeriodeCallback() {
const {
behandlingFormPrefix, perioder, periodeTyper, utsettelseArsaker,
} = this.props;
const {
uttakNyPeriodeFom, uttakNyPeriodeTom, uttakNyPeriodeType, uttakNyPeriodeAndel, uttakNyPeriodeArsak,
} = this.props.nyPeriode;
const getPeriodeData = (periode, periodeArray) => periodeArray
.filter(({ kode }) => kode === periode);
const periodeObjekt = getPeriodeData(uttakNyPeriodeType, periodeTyper)[0];
const arsakObjekt = getPeriodeData(uttakNyPeriodeArsak, utsettelseArsaker)[0];
const utsettelseĆ
rsak = arsakObjekt !== undefined ? {
kode: arsakObjekt.kode,
kodeverk: arsakObjekt.kodeverk,
navn: arsakObjekt.navn,
} : {};
const nyPeriode = [{
arbeidstidsprosent: uttakNyPeriodeAndel,
bekreftet: false,
fom: uttakNyPeriodeFom,
saksebehandlersBegrunnelse: null,
tom: uttakNyPeriodeTom,
utsettelseĆ
rsak,
uttakPeriodeType: {
kode: periodeObjekt.kode,
kodeverk: periodeObjekt.kodeverk,
navn: periodeObjekt.navn,
},
}];
this.props.reduxFormChange(`${behandlingFormPrefix}.UttakInfoPanel`, 'perioder', perioder.concat(nyPeriode)
.sort((a, b) => a.fom > b.fom));
this.setState({ isNyPeriodeFormOpen: !this.state.isNyPeriodeFormOpen });
}
removePeriodCallback(index) {
const { behandlingFormPrefix, perioder } = this.props;
this.props.reduxFormChange(
`${behandlingFormPrefix}.UttakInfoPanel`, 'perioder',
perioder.filter((e, i) => i === index)
.sort((a, b) => a.fom > b.fom),
);
}
I am sending these two functions as callbacks to different components:
<FieldArray
name="perioder"
component={UttakPeriode}
removePeriodCallback={this.removePeriodCallback}
inntektsmelding={inntektsmelding}
/>
<UttakNyPeriode newPeriodeCallback={this.newPeriodeCallback} />
The problem I have is when I am clicking on a button in the component where I am sending the newPeriodeCallback:
<Hovedknapp
className={styles.oppdaterMargin}
htmlType="button"
mini
onClick={newPeriodeCallback}
>
In the container component on inspecting the console I see that the removePeriodCallback is also being triggered which then removes the new period that is being added in the function newPeriodeCallback. Why is this happening, and how can I fix this?
Update
I have tried by following the suggestion in the comment, to use the arrow function in onClick, like this:
<Image
className={styles.removeIcon}
src={removePeriod}
onClick={() => removePeriodCallback(index)}
alt="Slett periode"
/>
And that has stopped the function from triggering from other places, but it is not triggering onClick either. What is wrong with this code?
This is the complete child component:
export const UttakPeriodeType = ({
bekreftet,
tilDato,
fraDato,
uttakPeriodeType,
removePeriodCallback,
index,
}) => (
<div className={classNames('periodeType', { active: !bekreftet })}>
<div className={styles.headerWrapper}>
<Element>{uttakPeriodeType.navn}</Element>
<div>
<Image src={editPeriod} alt="Rediger periode" />
<Image
className={styles.removeIcon}
src={removePeriod}
onClick={() => removePeriodCallback(index)}
alt="Slett periode"
/>
</div>
</div>
<div>
And this is the image component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';
import Tooltip from 'sharedComponents/Tooltip';
export class Image extends Component {
constructor() {
super();
this.state = {
isHovering: false,
};
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
}
onFocus() {
this.setState({ isHovering: true });
}
onBlur() {
this.setState({ isHovering: false });
}
onKeyDown(e) {
if (e.key === 'Enter' || e.key === ' ') {
this.props.onKeyDown(e);
e.preventDefault();
}
}
render() {
const image = (<img // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
className={this.props.className}
src={this.props.src !== null ? this.props.src : this.props.imageSrcFunction(this.state.isHovering)}
alt={this.props.altCode ? this.props.intl.formatMessage({ id: this.props.altCode }) : this.props.alt}
title={this.props.titleCode ? this.props.intl.formatMessage({ id: this.props.titleCode }) : this.props.title}
tabIndex={this.props.tabIndex}
onMouseOver={this.onFocus}
onMouseOut={this.onBlur}
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeyDown={this.onKeyDown}
onMouseDown={this.props.onMouseDown}
/>);
if (this.props.tooltip === null) {
return image;
}
return (
<Tooltip header={this.props.tooltip.header} body={this.props.tooltip.body}>
{image}
</Tooltip>
);
}
}
Image.propTypes = {
className: PropTypes.string,
src: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape(),
]),
imageSrcFunction: PropTypes.func,
onMouseDown: PropTypes.func,
onKeyDown: PropTypes.func,
alt: PropTypes.string,
altCode: PropTypes.string,
title: PropTypes.string,
titleCode: PropTypes.string,
tabIndex: PropTypes.string,
tooltip: PropTypes.shape({
header: PropTypes.string.isRequired,
body: PropTypes.string,
}),
intl: intlShape.isRequired,
};
Image.defaultProps = {
className: '',
src: null,
imageSrcFunction: null,
onMouseDown: null,
onKeyDown: null,
tabIndex: null,
tooltip: null,
alt: null,
altCode: null,
title: null,
titleCode: null,
};
export default injectIntl(Image);
Related
I used Ant table to show some information.
https://codesandbox.io/s/proud-architecture-lsb85?file=/src/index.js
I want to customize the position of the checkbox for row selection.
In this application, you can see the header in the following order of checkbox, Name, Age, Address but I want to swap checkbox and Name.
You can add checkbox columns and customize render and titleRender of it to checkbox and then handle the events. if you incounter performance issue you have to add some memoization on columns or evenet handlers.
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import { Table, Button, Checkbox } from "antd";
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`
});
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedRowKeys: [], // Check here to configure the default column
loading: false,
allChecked: false
};
this.columns = [
{
title: "Name",
dataIndex: "name"
},
{
dataIndex: "checked",
title: () => {
return (
<Checkbox
checked={this.state.allChecked}
onChange={(e) => this.selectAll(e)}
></Checkbox>
);
},
render: (text, rec, index) => {
return (
<Checkbox
checked={
this.state.selectedRowKeys.includes(rec.key) ||
this.state.allChecked
}
onChange={(e) => this.onChange(e, rec)}
></Checkbox>
);
}
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
}
start = () => {
this.setState({ loading: true });
// ajax request after empty completing
setTimeout(() => {
this.setState({
selectedRowKeys: [],
loading: false
});
}, 1000);
};
onChange = (e, rec) => {
const checked = e.target.checked;
if (checked) {
this.setState((state) => ({
...state,
selectedRowKeys: [...state.selectedRowKeys, rec.key]
}));
} else {
this.setState((state) => ({
...state,
selectedRowKeys: [
...state.selectedRowKeys.filter((item) => item !== rec.key)
]
}));
}
};
selectAll = (e) => {
const checked = e.target.checked;
if (checked) {
this.setState((state) => ({
...state,
allChecked: true
}));
} else {
this.setState((state) => ({
...state,
allChecked: false
}));
}
};
onSelectChange = (selectedRowKeys) => {
console.log("selectedRowKeys changed: ", selectedRowKeys);
this.setState({ selectedRowKeys });
};
render() {
const { loading, selectedRowKeys } = this.state;
const hasSelected = selectedRowKeys.length > 0;
return (
<div>
<div style={{ marginBottom: 16 }}>
<Button
type="primary"
onClick={this.start}
disabled={!hasSelected}
loading={loading}
>
Reload
</Button>
<span style={{ marginLeft: 8 }}>
{hasSelected ? `Selected ${selectedRowKeys.length} items` : ""}
</span>
</div>
<Table columns={this.columns} dataSource={data} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
I have a react code
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import SelectBox from '../SelectBox';
import Icon from '../Icon';
import Input from '../Input';
import CountryItem from './components/CountryItem';
const PhoneNumber = props => {
const { children, className } = props;
const [selectedOption, setSelectedOption] = useState('');
const [inputParam, setInputParam] = useState('');
const inputParamChangeHandler = event => {
const inputChar = event.target.value;
setInputParam(inputChar);
console.log({ inputChar });
};
const selectedOptionChangeHandler = event => {
const valueSelected = event.target.value;
setSelectedOption(valueSelected);
console.log({ valueSelected });
};
console.log({ children });
const childrenWithPropsFromParent = React.Children.map(children, child => child);
const optionsWithImage = [
{
value: '+880',
label: <CountryItem />,
},
{
value: '+55',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">Brazil</span>
<span className="phone-number__country-code">(+55)</span>
</span>
</span>
),
},
{
value: '+40',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">Romania</span>
<span className="phone-number__country-code">(+40)</span>
</span>
</span>
),
},
{
value: '+44',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">United Kingdom</span>
<span className="phone-number__country-code">(+44)</span>
</span>
</span>
),
},
{
value: '+1',
label: (
<span>
<span className="phone-number__country-flag">
<Icon name="home" />
</span>
<span>
<span className="phone-number__country-name">Bahamas</span>
<span className="phone-number__country-code">(+1)</span>
</span>
</span>
),
},
];
return (
<div className={classNames('phone-number', className)}>
<div>Phone Number</div>
<div className="phone-number__wrapper">
<SelectBox
size="small"
value={selectedOption}
touched={false}
onChange={selectedOptionChangeHandler}
isSearchable={false}
isDisabled={false}
isClearable={false}
options={optionsWithImage}
width="small"
/>
<Input value={inputParam} onChange={inputParamChangeHandler} placeholder="XXXX XXX XXX" />
</div>
{/* {childrenWithPropsFromParent} */}
</div>
);
};
PhoneNumber.defaultProps = {
children: null,
className: null,
};
PhoneNumber.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
};
export default PhoneNumber;
SelectBox Component:
import React, { Component } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import Select from 'react-select';
import CreatableSelect from 'react-select/creatable';
import './select-box.styles.scss';
import colorOptions from '../../helpers/colorOptions';
import Tooltip from '../Tooltip';
import Icon from '../Icon';
const sizeBasedSelector = (element, size) => {
switch (size) {
case 'tiny':
return `select-box__${element}--tiny`;
case 'small':
return `select-box__${element}--small`;
case 'large':
return `select-box__${element}--large`;
default:
return null;
}
};
class SelectBox extends Component {
constructor(props) {
super(props);
this.state = {
selectedOption: props.value,
};
}
customTheme = theme => {
const { primary, primary75, primary50, primary25 } = this.props.colors;
return {
...theme,
colors: {
...theme.colors,
primary,
primary75,
primary50,
primary25,
},
};
};
renderPlaceHolder = () => {
const { preIcon, placeholderText, size } = this.props;
return (
<div className="select-box__placeholder">
{preIcon && (
<span className={classnames('select-box__pre-icon', sizeBasedSelector('pre-icon', size))}>
{preIcon}
</span>
)}
<span
className={classnames(
'select-box__placeholder-text',
sizeBasedSelector('placeholder-text', size)
)}
>
{placeholderText}
</span>
</div>
);
};
handleChange = selectedOption => {
this.setState({ selectedOption });
this.props.onChange(selectedOption);
};
handleInputChange = inputValue => {
this.props.onInputChange(inputValue);
};
getFlags = () => {
const { isClearable, isDisabled, isMulti, isSearchable } = this.props;
return {
isClearable,
isDisabled,
isMulti,
isSearchable,
};
};
render() {
const { selectedOption } = this.state;
const {
autoFocus,
className,
classNamePrefix,
colors,
errorMsg,
label,
name,
options,
size,
touched,
isMulti,
isDisabled,
isCreatable,
width,
required,
} = this.props;
const hasError = touched && errorMsg;
const selectProps = {
...this.getFlags(),
autoFocus,
className: classnames(
`${className} ${className}--${size}`,
sizeBasedSelector('width', width),
{
'select-box-container--notMulti': !isMulti,
'select-box-container--error': hasError,
}
),
classNamePrefix,
colors,
theme: this.customTheme,
value: selectedOption,
name,
options,
onInputChange: this.handleInputChange,
onChange: this.handleChange,
placeholder: this.renderPlaceHolder(),
required,
};
return (
<>
{label && (
<span
className={classnames(`select-box__label select-box__label--${size}`, {
'select-box__label--required': required,
'select-box__label--disabled': isDisabled,
})}
>
{label}
</span>
)}
<div className="select-box-wrapper">
{isCreatable ? <CreatableSelect {...selectProps} /> : <Select {...selectProps} />}
{errorMsg && (
<Tooltip
classNameForWrapper="select-box__tooltip-wrapper"
classNameForTooltip="select-box__tooltip"
content={errorMsg}
position="bottom-right"
gap={0}
>
<Icon name="invalid" />
</Tooltip>
)}
</div>
</>
);
}
}
SelectBox.defaultProps = {
autoFocus: false,
className: 'select-box-container',
classNamePrefix: 'select-box',
colors: {
primary: colorOptions.coolGray20,
primary75: colorOptions.coolGray45,
primary50: colorOptions.coolGray70,
primary25: colorOptions.coolGray95,
},
errorMsg: '',
isClearable: true,
isCreatable: false,
isDisabled: false,
isMulti: false,
isSearchable: true,
label: '',
name: 'select-box',
onChange: () => {},
onInputChange: () => {},
options: [],
placeholderText: 'Select...',
preIcon: null,
required: false,
size: 'small',
touched: false,
value: null,
width: 'small',
};
SelectBox.propTypes = {
autoFocus: PropTypes.bool,
className: PropTypes.string,
classNamePrefix: PropTypes.string,
colors: PropTypes.shape({
primary: PropTypes.string,
primary75: PropTypes.string,
primary50: PropTypes.string,
primary25: PropTypes.string,
}),
errorMsg: PropTypes.string,
isClearable: PropTypes.bool,
isCreatable: PropTypes.bool,
isDisabled: PropTypes.bool,
isMulti: PropTypes.bool,
isSearchable: PropTypes.bool,
label: PropTypes.string,
name: PropTypes.string,
onChange: PropTypes.func,
onInputChange: PropTypes.func,
options: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.string,
})
),
preIcon: PropTypes.node,
placeholderText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
value: PropTypes.oneOf([
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})
),
]),
required: PropTypes.bool,
size: PropTypes.oneOf(['tiny', 'small', 'large']),
touched: PropTypes.bool,
width: PropTypes.oneOf(['tiny', 'small', 'large', 'full']),
};
export default SelectBox;
now I want to take the value of the selectedOption from the SelectBox component but this current code base giving me error while I select a option from the SelectBox. The error is
Uncaught TypeError: Cannot read property 'value' of undefined
at Object.selectedOptionChangeHandler [as onChange] (PhoneNumber.jsx:22)
at Object.onChange (SelectBox.jsx:71)
at StateManager.callProp (stateManager-04f734a2.browser.esm.js:98)
at StateManager._this.onChange (stateManager-04f734a2.browser.esm.js:38)
at Select._this.onChange (Select-9fdb8cd0.browser.esm.js:1079)
at Select._this.setValue (Select-9fdb8cd0.browser.esm.js:1106)
at Select._this.selectOption (Select-9fdb8cd0.browser.esm.js:1155)
at onSelect (Select-9fdb8cd0.browser.esm.js:1732)
at HTMLUnknownElement.callCallback (react-dom.development.js:336)
at Object.invokeGuardedCallbackDev (react-dom.development.js:385)
How to get the value from onChange from the SelectBox using the current implementation?
Your SelectBox component's handleChange callback outputs the selected value versus an event, so you should consume the value.
SelectBox
handleChange = selectedOption => {
this.setState({ selectedOption });
this.props.onChange(selectedOption); // <-- sends selected value!
};
PhoneNumber
const selectedOptionChangeHandler = valueSelected => { // <-- consume valueSelected
setSelectedOption(valueSelected);
console.log({ valueSelected });
};
I have a form that uses a child component with input fields. I have used props to get the data from the parent component, but it's not adding the prop value to the input field. There is no errors in the console.
Parent component:
const {
address,
errors
} = this.state;
return (
<form noValidate autoComplete="off" onSubmit={this.onSubmit.bind(this)}>
<LocationInputGroup
errors={errors}
address={address}
/>
<Button
type="submit"
style={{ marginRight: 10, marginTop: 20 }}
variant="callToAction"
> Submit</Button>
</form>
);
Child component:
constructor(props) {
super(props);
this.state = {
address: "",
errors: {}
};
}
componentDidMount() {
this.setState({
errors: this.props.errors,
address: this.props.address
});
}
render() {
const {
address,
errors
} = this.state;
return (
<div>
<InputGroup
value={address}
error={errors.address}
label="Address"
name={"address"}
onChange={e => this.setState({ address: e.target.value })}
placeholder={"Address"}
/>
</div>
);
}
InputGroup component:
class InputGroup extends Component {
constructor(props) {
super(props);
}
//TODO check if scrolling still changes with number inputs
//Bug was in Chrome 73 https://www.chromestatus.com/features/6662647093133312
//If it's no longer a bug these listeners can be removed and the component changed back to a stateless component
handleWheel = e => e.preventDefault();
componentDidMount() {
if (this.props.type === "number") {
ReactDOM.findDOMNode(this).addEventListener("wheel", this.handleWheel);
}
}
componentWillUnmount() {
if (this.props.type === "number") {
ReactDOM.findDOMNode(this).removeEventListener("wheel", this.handleWheel);
}
}
render() {
const {
disabled,
classes,
error,
value,
name,
label,
placeholder,
type,
isSearch,
onChange,
onBlur,
onFocus,
multiline,
autoFocus,
InputProps = {},
autoComplete,
allowNegative,
labelProps
} = this.props;
if (type === "phone") {
InputProps.inputComponent = PhoneNumberInputMask;
}
let onChangeEvent = onChange;
//Stop them from entering negative numbers unless they explicitly allow them
if (type === "number" && !allowNegative) {
onChangeEvent = e => {
const numberString = e.target.value;
if (!isNaN(numberString) && Number(numberString) >= 0) {
onChange(e);
}
};
}
return (
<FormControl
className={classes.formControl}
error
aria-describedby={`%${name}-error-text`}
>
<FormatInputLabel {...labelProps}>{label}</FormatInputLabel>
<TextField
error={!!error}
id={name}
type={type}
value={value}
onChange={onChangeEvent}
margin="normal"
onBlur={onBlur}
onFocus={onFocus}
InputProps={{
...InputProps,
classes: {
input: classnames({
[classes.input]: true,
[classes.searchInput]: isSearch
})
}
}}
placeholder={placeholder}
multiline={multiline}
autoFocus={autoFocus}
disabled={disabled}
autoComplete={autoComplete}
onWheel={e => e.preventDefault()}
/>
<FormHelperText
className={classes.errorHelperText}
id={`${name}-error-text`}
>
{error}
</FormHelperText>
</FormControl>
);
}
}
InputGroup.defaultProps = {
value: "",
type: "text",
labelProps: {}
};
InputGroup.propTypes = {
error: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
name: PropTypes.string.isRequired,
label: PropTypes.string,
placeholder: PropTypes.string,
type: PropTypes.string,
isSearch: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func,
onFocus: PropTypes.func,
multiline: PropTypes.bool,
autoFocus: PropTypes.bool,
InputProps: PropTypes.object,
disabled: PropTypes.bool,
autoComplete: PropTypes.string,
allowNegative: PropTypes.bool,
labelProps: PropTypes.object
};
export default withStyles(styles)(InputGroup);
I hope someone can advise what the issue is and how to overcome it.
Normally when we pass data from parent, we consider it to be Immutable,
(i.e) It should not be changed directly from the child components.
So, what one could do is use the prop value directly from the parent and use a method to mutate or change from the parent.
Below code is self explanatory.
Parent
class Parent {
// parent immutable state
public parentObject: object = {
location: "",
phoneNo: ""
};
constructor() {
this.state = {
parentObject: this.parentObject
}
}
public onParentObjectChangeCallBack(key: string, value: string | number) {
this.state.parentObject[key] = value;
// to rerender
this.setState();
}
public render () {
return <ChildComponent parentObject={this.state.parentObject}
onChange={this.onParentObjectChangeCallBack} />
}
}
Child
class ChildComponent {
#Prop
public parentObject: object;
#Prop
public onChange: Function;
public render() {
<div>
{
this.parentObject.keys.map((key) => {
return <div>
<span> {{key}}</span>
// calls parent method to change the immutable object
<input value={{this.parentObject[key]}} onChange=(val) =>
this.onChange(key, val) />
</div>
})
}
</div>
}
}
I have a situation where in I am having multiple on click events that are fired on a column header. There will be one event for filter dropdown and the other one for sorting. There is a filter icon , on click of which column filter options will be shown. And on click of the header, sorting should also happen.
Now whenever I click on the filter icon, both handlers are getting fired. Can someone help me with this.
On click of filter icon, only filter handler should fire
Help would be appreciated.
Sandbox: https://codesandbox.io/s/relaxed-feather-xrpkp
Parent
import * as React from "react";
import { render } from "react-dom";
import ReactTable from "react-table";
import "./styles.css";
import "react-table/react-table.css";
import Child from "./Child";
interface IState {
data: {}[];
columns: {}[];
}
interface IProps {}
export default class App extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
data: [
{ firstName: "Jack", status: "Submitted", age: "14" },
{ firstName: "Simon", status: "Pending", age: "15" },
{ firstName: "Pete", status: "Approved", age: "17" }
],
columns: []
};
}
handleColumnFilter = (value: any) => {
console.log(value);
};
sortHandler = () => {
console.log("sort handler");
};
componentDidMount() {
let columns = [
{
Header: () => (
<div onClick={this.sortHandler}>
<div style={{ position: "absolute", marginLeft: "10px" }}>
<Child handleFilter={this.handleColumnFilter} />
</div>
<span>First Name</span>
</div>
),
accessor: "firstName",
sortable: false,
show: true,
displayValue: " First Name"
},
{
Header: () => (
<div onClick={this.sortHandler}>
<span>Status</span>
</div>
),
accessor: "status",
sortable: false
}
];
this.setState({ columns });
}
render() {
const { data, columns } = this.state;
return (
<div>
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
Filter Component
import * as React from "react";
import { Icon } from "semantic-ui-react";
import "./styles.css";
interface IProps {
handleFilter(val1: any): void;
}
interface IState {
showList: boolean;
}
export default class Child extends React.Component<IProps, IState> {
constructor(props: any) {
super(props);
this.state = {
showList: false
};
}
toggleList = () => {
console.log("filter handler");
this.setState(prevState => ({ showList: !prevState.showList }));
};
render() {
let { showList } = this.state;
let visibleFlag: string;
if (showList === true) visibleFlag = "visible";
else visibleFlag = "";
return (
<div>
<div style={{ position: "absolute" }}>
<div
className={"ui scrolling dropdown column-settings " + visibleFlag}
>
<Icon className="filter" onClick={this.toggleList} />
</div>
</div>
</div>
);
}
}
You just need event.stopPropagation(). This will isolate the event to only this specific execution-block. So now when you click on the filter-icon, it will only trigger the designated event-handler.
toggleList = (event) => {
event.stopPropgation()
console.log("filter handler");
this.setState(prevState => ({ showList: !prevState.showList }));
};
You'll also need to use it here as well:
handleValueChange = (event: React.FormEvent<HTMLInputElement>, data: any) => {
event.stopPropagation()
let updated: any;
if (data.checked) {
updated = [...this.state.selected, data.name];
} else {
updated = this.state.selected.filter(v => v !== data.name);
}
this.setState({ selected: updated });
};
And here:
passSelectionToParent = (event: any) => {
event.preventDefault();
event.stopPropagation()
this.props.handleFilter(this.props.name, this.state.selected);
};
Literally, anytime you have a click-event for a parent-markup and it has children mark-up that also have a click-event, you can use event.stopPropagation() to stop the parent click-event from firing.
Here's the sandbox: https://codesandbox.io/s/elated-gauss-wgf3t
// index.js
import React, { Component } from "react";
import MaterialTable, { MTableEditRow } from "material-table";
import axios from "axios";
import DataModel from "./DataModel";
import TitleInput from "./TitleInput";
class Report extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
workOrderOptions: [],
newEntry: {
userId: "",
id: "",
title: "",
body: ""
}
};
this.handleNewTitle = this.handleNewTitle.bind(this);
this.cancelAdd = this.cancelAdd.bind(this);
}
renderData() {
const URL = "https://jsonplaceholder.typicode.com/posts";
axios
.get(URL)
.then(response => {
this.setState({
data: response.data
});
})
.catch(error => {
console.log("ERROR:", error);
});
}
// I want to fire this method upon canceling the "add row"
cancelAdd() {
this.setState({
newEntry: {
userId: "",
id: "",
title: "",
body: ""
}
});
}
handleNewTitle(title) {
this.setState({
newEntry: {
// ...this.state.newEntry.title,
title: title
}
});
}
componentDidMount() {
this.renderData();
}
render() {
const columns = [
{
title: "ID",
field: "id",
editable: "never"
},
{
title: "User ID",
field: "userId",
editable: "never"
},
{
title: "Title",
field: "title",
editable: "never"
},
{
title: "Body",
field: "body",
editable: "never"
}
];
if (this.state.data) {
return (
<div>
<MaterialTable
components={{
EditRow: props => {
return (
<div>
<TitleInput
value={this.state.newEntry.title}
title={this.handleNewTitle}
/>
{/* <BodyInput
value={this.state.newEntry.body}
body={this.handleNewBody}
/>, <UserIDInput />, etc... */}
<MTableEditRow
{...props}
data={this.state.newEntry}
// Is there a handleCancelAction (or something ma something)?
</div>
);
}
}}
editable={{
// Just a sample add
onRowAdd: newData =>
new Promise((resolve, reject) => {
const result = {
id: 15465,
userId: 87946542,
title: this.state.newEntry.title,
body: "Old man Santiago"
};
console.log(result);
const data = this.state.data;
data.push(result);
this.setState({
...this.state
});
resolve();
})
}}
data={this.state.data}
columns={columns}
title={"Title"}
/>
</div>
);
} else if (!this.state.data) {
return <div>Loading...</div>;
}
}
}
export default Report;
// TitleInput.js
import React, { Component } from "react";
class TitleInput extends Component {
constructor(props) {
super(props);
this.handleTitleChanges = this.handleTitleChanges.bind(this);
}
handleTitleChanges(event) {
const title = event.target.value;
this.props.title(title);
}
render() {
return (
<div>
<select onChange={this.handleTitleChanges}>
<option selected hidden />
<option value="Old Man and the Sea">Old Man and the Sea</option>
<option value="Where the Red Fern Grows">
Where the Red Fern Grows
</option>
<option value="Nineteen Eighty-Four">Nineteen Eighty-Four</option>
<option value="The Kite Runner">The Kite Runner</option>
</select>
</div>
);
}
}
export default TitleInput;
// DataModel.js
export const DataModel = {
userId: "",
id: "",
title: "",
body: ""
};
You can see the sandbox example here: https://codesandbox.io/embed/festive-engelbart-7ned7
<MTableEditRow
{...props}
data={this.state.newEntry}
// on the onEditingCanceled prop, you can access the cancel method
// in this instance, we're clearing the state and then calling the
// method provided by the prop to close the showAddRow, we're passing
// mode, which will return "add"
onEditingCanceled={(mode, rowData) => {
this.cancelAdd();
props.onEditingCanceled(mode);
}}
/>
Line 309, (onEditingCanceled): https://github.com/mbrn/material-table/blob/master/src/material-table.js
// index.js
import React, { Component } from "react";
import MaterialTable, { MTableEditRow } from "material-table";
import axios from "axios";
import DataModel from "./DataModel";
import TitleInput from "./TitleInput";
class Report extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
workOrderOptions: [],
newEntry: {
userId: "",
id: "",
title: "",
body: ""
}
};
this.handleNewTitle = this.handleNewTitle.bind(this);
this.cancelAdd = this.cancelAdd.bind(this);
}
renderData() {
const URL = "https://jsonplaceholder.typicode.com/posts";
axios
.get(URL)
.then(response => {
this.setState({
data: response.data
});
})
.catch(error => {
console.log("ERROR:", error);
});
}
// I want to fire this method upon canceling the "add row"
cancelAdd() {
this.setState({
newEntry: {
userId: "",
id: "",
title: "",
body: ""
}
});
}
handleNewTitle(title) {
this.setState({
newEntry: {
// ...this.state.newEntry.title,
title: title
}
});
}
componentDidMount() {
this.renderData();
}
render() {
const columns = [
{
title: "ID",
field: "id",
editable: "never"
},
{
title: "User ID",
field: "userId",
editable: "never"
},
{
title: "Title",
field: "title",
editable: "never"
},
{
title: "Body",
field: "body",
editable: "never"
}
];
if (this.state.data) {
return (
<div>
<MaterialTable
components={{
EditRow: props => {
return (
<div>
<TitleInput
value={this.state.newEntry.title}
title={this.handleNewTitle}
/>
{/* <BodyInput
value={this.state.newEntry.body}
body={this.handleNewBody}
/>, <UserIDInput />, etc... */}
<MTableEditRow
{...props}
data={this.state.newEntry}
// looks like there is with onEditingCanceled
onEditingCanceled={(mode, rowData) => {
this.cancelAdd();
props.onEditingCanceled(mode);
}}
/>
</div>
);
}
}}
editable={{
// Just a sample add
onRowAdd: newData =>
new Promise((resolve, reject) => {
const result = {
id: 15465,
userId: 87946542,
title: this.state.newEntry.title,
body: "Old man Santiago"
};
console.log(result);
const data = this.state.data;
data.push(result);
this.setState({
...this.state
});
resolve();
})
}}
data={this.state.data}
columns={columns}
title={"Title"}
/>
</div>
);
} else if (!this.state.data) {
return <div>Loading...</div>;
}
}
}
export default Report;