How to trigger onCellEditApproved inside EditField onChange handler in React's MaterialTable? - javascript

I am trying to trigger onCellEditApproved when the value of the editable column changes (or onBlur) inside MaterialTable
cellEditable={{
cellStyle: {},
onCellEditApproved: (newValue, oldValue, rowData, columnDef) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
let updatedRow = rowData
updatedRow.anQty = parseInt(newValue)
const updatedRowData = {
...rowData,
[columnDef.anQty]: newValue,
};
setGetCompanyData(prevData => {
const index = prevData.indexOf(rowData);
return [...prevData.slice(0, index), updatedRowData, ...prevData.slice(index + 1)];
});
}, 1000);
});
}
}}
onCellEditApproved is triggered when the user press "Enter" and I want to override that behavior with onChange and onBlur, that's why I made a custom EditField:
components={{
EditField: props => {
const handleChange = (event) => {
//const { onCellEditApproved } = props;
props.onChange(event.target.value);
//onCellEditApproved(props.value, props.oldValue, props.rowData, props.columnDef);
}
return <input {...props} type="text" className="test" onChange={handleChange} />
},
}}
I have tried to get onCellEditApproved event via props inside of EditField but unsuccessfully.

Related

How to add a new editable row in React-Table?

I'm building a dynamic table using React-Table and i want to add a new row of editable cells.
At the moment i can add new row but only when i press the global edit button i can edit it, instead i want to add a row which would be editable at first.
This is my code -
Main component
function StyledTable() {
useEffect(() => {
dispatch(getData(mokeJsonData));
}, []);
const [datatoColumns] = useState(columnDataaa.slice(1));
const [skipPageReset, setSkipPageReset] = useState(false);
const data = useSelector((state) => state.dataReducer.data);
const dispatch = useDispatch();
const columns = useMemo(
() => [
{
Header: "",
id: "expander",
Cell2: ({ row }) => {
return (
<span {...row.getToggleRowExpandedProps()}>
{row.isExpanded ? "-" : "+"}
</span>
);
},
Cell: () => {
return <div></div>;
},
},
{
Header: columnDataaa[0].Header,
accessor: columnDataaa[0].accessor,
Cell: ({ value, row }) => {
return (
<FlexDiv>
<HighlightOffIcon
style={{ marginRight: "5px", color: "grey", width: "20px" }}
onClick={() => dispatch(deleteRow(row.index))}
/>
{value}
</FlexDiv>
);
},
},
...datatoColumns,
],
[]
);
useEffect(() => {
setSkipPageReset(false);
}, [data]);
const renderRowSubComponent = useCallback(
({ row }) => ({
values: row.original.addInfo && row.original.addInfo,
}),
[]
);
return (
<Styles>
<h1>הגדרת מנהל</h1>
<Table
columns={columns}
skipPageReset={skipPageReset}
renderRowSubComponent={renderRowSubComponent}
/>
</Styles>
);
}
export default StyledTable;
Editable Cell
const EditableCell = ({
value: initialValue,
row: { index },
column: { id, editable, type, width, valueOptions },
}) => {
const [value, setValue] = useState(initialValue);
const onChange = (e) => {
setValue(e.target.value);
};
const dispatch = useDispatch();
const onBlur = () => {
if (value === "") return alert("requiredddd");
return dispatch(updateMyData({ index, id, value }));
};
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
if (type === "singleSelect")
return (
<InputSelect
value={value}
onChange={onChange}
onBlur={onBlur}
style={{ width: width }}
>
{valueOptions.map((item, i) => {
return (
<option value={item.label} key={i}>
{item.label}
</option>
);
})}
</InputSelect>
);
if (type === "date")
return (
<DatePicker
style={{ width: width }}
type="date"
disabled={editable === false}
value={value}
onChange={onChange}
onBlur={onBlur}
/>
);
return (
<input
style={{ width: width }}
disabled={editable === false}
value={value}
onChange={onChange}
onBlur={onBlur}
/>
);
};
export default EditableCell;
Add Row function
addRow: (state, action) => {
const obj = {};
action.payload.slice(1).forEach((item) => {
obj[item.accessor] = '';
});
if (
obj &&
Object.keys(obj).length === 0 &&
Object.getPrototypeOf(obj) === Object.prototype
)
return;
else {
state.data.splice(0, 0, obj);
state.originalData = state.data;
}
},
Thanks
Pass the state variable and method to the useTable() root hook. custom plugin hooks and other variables/methods maintaining the component state are returned from the table instance. These you can later retrieve from anywhere you want.
const {
// all your hooks...
} = useTable(
{
columns,
data,
// all your other hooks...
updateMyData,
// pass state variables so that we can access them in edit hook later
editableRowIndex, // index of the single row we want to edit
setEditableRowIndex // setState hook for toggling edit on/off switch
},
// other hooks...
(hooks) => {
hooks.allColumns.push((columns) => [
// other hooks such as selection hook
...columns,
// edit hook
{
accessor: "edit",
id: "edit",
Header: "edit",
Cell: ({ row, setEditableRowIndex, editableRowIndex }) => (
<button
className="action-button"
onClick={() => {
const currentIndex = row.index;
if (editableRowIndex !== currentIndex) {
// row requested for edit access
setEditableRowIndex(currentIndex);
} else {
// request for saving the updated row
setEditableRowIndex(null); // keep the row closed for edit after we finish updating it
const updatedRow = row.values;
console.log("updated row values:");
console.log(updatedRow);
// call your updateRow API
}
}}
>
{/* single action button supporting 2 modes */}
{editableRowIndex !== row.index ? "Edit" : "Save"}
</button>
)
}
]);
}
);
you can found example from bellow link
github repo link: https://github.com/smmziaul/only-one-row-editable
code sandbox link: https://codesandbox.io/s/github/smmziaul/only-one-row-editable

unable to remove first character from search in input react js

I have made a search field with debouncing. Everything works fine but when I try to empty the search field with backspace it continuously re-show all characters and does not remove them(the first character is always there).
you can see it in the attached gif
my parent component
class ParentComponent extends React.Component {
this.queryParam = {
keyword: ''
}
keywordSearch = value => {
const {
history: { push },
match: { url },
location: { search },
} = this.props;
queryParams = {...queryParams, keyword: value, };
push(`${url}?${queryString})
};
render() {
<SearchComponent
value={this.queryParams.keyword}
onUpdate={this.keywordSearch}
/>
}
}
my search field component
const SearchComponent = ({ value, onUpdate }) => {
const [fieldValue, setFieldValue] = useState(value);
const handleChange = ({ target: { value } }) => {
debounceFunc(() => {
onUpdate(value);
}, 300);
setFieldValue(value);
};
return (
<Input
value={fieldValue || value}
disableUnderline
onChange={handleChange}
className={classes.root}
placeholder='Search'
startAdornment={
<InputAdornment position="start">
<Search className={classes.icon} fontSize="small" />
</InputAdornment>
}
/>
}
here is my custom debounce component
export const debounceFunction = () => {
let timeOut = null;
return (callBack, wait) => {
if (timeOut) clearTimeout(timeOut);
timeOut = setTimeout(() => {
callBack();
}, wait);
};
};
export const debounceFunc = debounceFunction();
the problem is in this debounce function. can anyone help me in this regard? why it isn't removing the first character?
Thanks
First problem is <Input value={fieldValue || value}
=> use just the local state:
<Input value={fieldValue} to change the visible value immediatelly.
Second problem is this.queryParams.keyword being an instance property, not a React State
=> use this.state.... and this.setState(...) (or Hooks) to update debounced state in the parent
I have changed debounceFunction parameter and the way to use it. Can you give it a try
const SearchComponent = ({ value, onUpdate }) => {
const [fieldValue, setFieldValue] = useState(value);
const debouncedUpdate = debounceFunction(onUpdate, 300);
const handleChange = ({ target: { value } }) => {
debouncedUpdate(value);
setFieldValue(value);
};
return (
<Input
value={fieldValue || value}
disableUnderline
onChange={handleChange}
className={classes.root}
placeholder='Search'
startAdornment={
<InputAdornment position="start">
<Search className={classes.icon} fontSize="small" />
</InputAdornment>
}
/>
}
export const debounceFunction = (callBack, wait) => {
let timeOut = null;
return () => {
if (timeOut) clearTimeout(timeOut);
timeOut = setTimeout(() => {
callBack();
}, wait);
};
};
export const debounceFunc = debounceFunction();

How to capture option selected event in Material UI autocomplete component?

I am using the autocomplete component with filterOptions to suggest the creation of a new value as shown below:
<Autocomplete
multiple
name="participant-tags"
options={people}
getOptionLabel={(option) => option.name}
renderInput={(params) => {
return (
<TextField
{...params}
variant="outlined"
label="Participants"
/>
)
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
logger.debug('filterOptions(params) %j', params)
// Suggest the creation of a new value
if (params.inputValue !== '') {
filtered.push({
inputValue: params.inputValue,
name: `Add "${params.inputValue}"`,
});
}
return filtered;
}}
onKeyDown={(e) => {
if(e.keyCode === 13) {
// TODO: select currently highlighted option
e.preventDefault()
}
}}
onChange={(e, value, reason) => {
logger.debug(e.type)
logger.debug(value)
logger.debug(reason)
e.preventDefault()
}}
/>
However, I can't figure out where to handle the selection of the "Add this option" to actually add the option?
This was solved leveraging the 'reason' parameter in the onChange handler, and the onKeyDown handler isn't needed:
filterOptions={(options, params) => {
const filtered = filter(options, params);
if (params.inputValue !== '') {
filtered.push({
inputValue: params.inputValue,
[displayOptionsField]: `Add New ${newOptionLabel} "${params.inputValue}"`,
});
}
return filtered;
}}
onChange={(e, value, reason) => {
let newOptions
if (reason==='select-option') {
const last = value.pop();
if (last.inputValue) {
newOptions = value.concat([{[displayOptionsField]: last.inputValue}])
}
else {
newOptions = value.concat([last])
}
}
if (reason==='create-option') {
const last = value.pop();
newOptions = value.concat([{[displayOptionsField]: last}])
}
if (reason==='remove-option') {
newOptions = value
}
if (newOptions) {
onChange(newOptions)
}
}}
The onChange inside the onChange handler is there as a prop from a wrapping component.

update is not capturing and unable to update the input field

please find below code which contains name id and am rendering initially using map
am replacing id value to input type in UI
with the updated input type am trying to update the value onchange
update is not capturing and unable to update the input field
any suggestion?
please refer below snippet
import React, { useState } from "react";
const CstmInput = (props) => {
return (
<input
name={props.name}
type="text"
value={props.value}
onChange={(event) => props.onInputChange(event)}
/>
);
};
export default CstmInput;
import React, { useState } from "react";
import CstmInput from "./CstmInput";
const HierarcyTest = () => {
let rowData = [
{ name: "first", id: 10 },
{ name: "second", id: 20 },
];
const [data, setData] = useState(rowData);
const [name, setName] = useState({ fn: "test" });
const onInputChange = (e) => {
console.log("---event---", e.target.value);
setName({ ...name, fn: e.target.value });
};
let updateValue = () => {
let newData = data.map(
(item, index) =>
(item.id = (
<CstmInput name={item.name} value={item.id} onInputChange={(e) => onInputChange(e)} />
))
);
setData([...data, newData]);
};
return (
<div>
<div>Testing</div>
{data.map((val) => (
<h6>
{" "}
{val.name} {val.id}
</h6>
))}
<button onClick={updateValue}> Click </button>
</div>
);
};
export default HierarcyTest;
A few things why your code isn't working as intended:
1.
let updateValue = () => {
let newData = data.map((item, index) => {
if (item.id === 10) {
return [
(item.id = (
<CstmInput
value={item.id}
onInputChange={(e) => onInputChange(e)}
/>
)),
];
}
});
setData([...data, newData]);
};
In the above function inside the callback of map, you're only returning when a condition satisfies. Are you trying to filter the array instead? If not then return something when the if condition fails.
And why are you returning an array?
return [
(item.id = (
<CstmInput
value={item.id}
onInputChange={(e) => onInputChange(e)}
/>
)),
];
the above code seems logically wrong.
2.
const onInputChange = (e) => {
console.log("---event---", e.target.value);
setName({ ...name, fn: e.target.value });
};
If you want to update state which depends on the previous state then this is how you do it:
setName((prevState) => ({ ...prevState, fn: e.target.value }));
but since you're not actually relying on the properties of the previous state you can just use:
setName({fn: e.target.value });
Note that since your state only has one property and you want to update that single property you can completely overwrite the state, you don't need to spread the previous state.
update
change the updateValue function as the following:
let updateValue = () => {
setData(prevData => {
return prevData.map(el => {
return { ...el, id: <CstmInput value={el.id} onInputChange={(e) => onInputChange(e)} /> };
})
});
};
A stackblitz example I've created that implements what you're trying to do.

Trigger onChange event manually

I would like to call my onChange manually.
Here's my select with onChange (I pass onChangeSelect as a prop from parent component)
return(
<Form Control disabled={disabled}
<NativeSelect ref={ref} onChange={onChangeSelect}>
...
And I'd like to call that onChange every time my variable changes and is empty
useEffect(() => {
if (requiredSelect[0].length > 0 || id === "root1") { setDisabled(false) }
else { ref.current.value = ([[], []]); ref.current.onChange ; setDisabled(true); }
}, [requiredSelect])
And here's onChangeSelect in parent component
<Child onChangeSelect={(e) => rootChange(e)}>
and what it does
const rootChange = e => { setRootSelect(e.target.value.split(',')); }
The simplest solution here would be to change the definition of your rootChange function to accept the value instead of the event itself.
const rootChange = value => { setRootSelect(value.split(',')); }
// In parent:
<Child onChangeSelect={rootChange}>
// Select
<NativeSelect ref={ref} onChange={(e) => onChangeSelect(e.target.value)}>
You can trigger the function manually with:
onChangeSelect(whateverValueYouWant); // notice that you need the brackets when calling the function.
Answer in Typescript
//Child Component
type PropsType = {
onChange: (value: string) => void;
value: string;
};
const CustomInput: FC<PropsType> = (props: PropsType) => {
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
props.onChange(event.target.value);
};
return (<input
onChange={onChange}
type="text"
value={props.value}></input>);
};
//Parent Component
const [input, setInput] = React.useState('');
<CustomInput
onChange={(value: string) => {
setInput(value);
}}
value={input}></CustomInput>

Categories

Resources