I am learning React and I am trying to achieve the following:
I am using Dropdown list which allows a user to select multiple items. Then I am trying to add each selected option in an array. Then I am tying to display the selected items (options) in the render method, but it is not working.
Later on I would like to convert this component into a reusable one so I can perform cascading.
So, my questions are:
1) how can I add each selected option into an array
2) iterate through the array and display items inside the render method
3) how to make this component reusable
The image below shows that the length of the array is 1, but I can't display the items. It is empty.
Below I am including the code that I have so far:
import * as React from 'react';
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import { Dropdown, IDropdown, DropdownMenuItemType, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import './Dropdown.Basic.Example.scss';
import { IBaseProps,BaseComponent, createRef } from 'office-ui-fabric-react';
export interface IDropdownBasicExampleProps extends IBaseProps {
loadOptions: () => Promise<IDropdownOption[]>;
onChanged: (option: IDropdownOption, index?: number) => void;
}
export interface IDropdownBasicExampleState {
selectedItem?: IDropdownOption;
selectedItems: IDropdownOption[];
loading: boolean;
options: IDropdownOption[];
selectedKey: string | number;
error: string;
}
export class DropdownBasicExample extends BaseComponent<IDropdownBasicExampleProps,IDropdownBasicExampleState> {
private _basicDropdown = createRef<IDropdown>();
private selItem:IDropdownOption;
constructor(props:IDropdownBasicExampleProps,state:IDropdownBasicExampleState) {
super(props);
this.state = {
selectedItem: undefined,
selectedItems: new Array<IDropdownOption>(),
loading: true,
error: undefined,
options: undefined,
selectedKey:""
};
}
public componentDidMount(): void {
this.loadOptions();
}
private loadOptions(): void {
this.setState({
loading: true,
error: undefined,
options: undefined
});
this.props.loadOptions()
.then((options: IDropdownOption[]): void => {
this.setState({
loading: false,
error: undefined,
options: options
});
}, (error: any): void => {
this.setState((prevState: IDropdownBasicExampleState, props: IDropdownBasicExampleProps): IDropdownBasicExampleState => {
prevState.loading = false;
prevState.error = error;
return prevState;
});
});
}
public onChangeMultiSelect = (item: IDropdownOption, index): void => {
const updatedSelectedItem = this.state.selectedItems ? this.copyArray(this.state.selectedItems) : [];
if (item.selected) {
// add the option if it's checked
updatedSelectedItem.push(item.key);
} else {
// remove the option if it's unchecked
const currIndex = updatedSelectedItem.indexOf(item.key);
if (currIndex > -1) {
updatedSelectedItem.splice(currIndex, 1);
}
}
this.setState({
selectedItems: updatedSelectedItem,
//selectedItem:item
});
if (this.props.onChanged) {
this.props.onChanged(this.state.selectedItem, index);
}
console.log(this.state.selectedItems);
};
public copyArray = (array: any[]): any[] => {
const newArray: any[] = [];
for (let i = 0; i < array.length; i++) {
newArray[i] = array[i];
}
return newArray;
};
public render() {
const { selectedItem, selectedItems } = this.state;
return (
<div className="docs-DropdownExample">
<Dropdown
placeHolder="Select options"
label="Multi-Select controlled example:"
selectedKey={selectedItem ? selectedItem.key : undefined}
//selectedKeys={selectedItems}
onChanged={this.onChangeMultiSelect}
multiSelect
options={this.state.options}
/>
<div>length: {this.state.selectedItems.length}</div>
{this.state.selectedItems.map((item:IDropdownOption)=>{
return <div>key: {item.key} {item.selected} {item.text} {item.index}</div>
})}
</div>
);
}
}
First time define a state
this.state = {
checkbox_value: []
}
then define checkboxs such as:
<input onChange = { this.checkboxValue.bind(this) } value = "0" type="checkbox" />
<input onChange = { this.checkboxValue.bind(this) } value = "2" type="checkbox" />
then use the function.
checkboxValue(event){
let checked_list = this.state.checkbox_value;
let check = event.target.checked;
let value = event.target.value;
if(check){
this.setState({
checkbox_value: [...this.state.checkbox_value, value]
});
}else{
var index = checked_list.indexOf(event.target.value);
if (index > -1){
checked_list.splice(index, 1);
}
this.setState({
checkbox_value: checked_list
})
}
}
then you can show checked value in render using map() method for checkbox_value. I hope it is usefull for you
Related
basically attempting to create an Autocomplete feature for a booking engine the code is in class components want to convert it to a functional component with React Hooks.Have attempted to convert but my code is showing several warnings.can provide any code snippets if needed.
(how do you convert this.state and destructure the this keyword)
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
class AutocompleteClass extends Component {
static propTypes = {
suggestions: PropTypes.instanceOf(Array)
};
static defaultProps = {
suggestions: []
};
constructor(props) {
super(props);
this.state = {
// The active selection's index
activeSuggestion: 0,
// The suggestions that match the user's input
filteredSuggestions: [],
// Whether or not the suggestion list is shown
showSuggestions: false,
// What the user has entered
userInput: ""
};
}
onChange = e => {
const { suggestions } = this.props;
const userInput = e.currentTarget.value;
// Filter our suggestions that don't contain the user's input
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.value
});
};
onClick = e => {
this.setState({
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText
});
};
onKeyDown = e => {
const { activeSuggestion, filteredSuggestions } = this.state;
// User pressed the enter key
if (e.keyCode === 13) {
this.setState({
activeSuggestion: 0,
showSuggestions: false,
userInput: filteredSuggestions[activeSuggestion]
});
}
// User pressed the up arrow
else if (e.keyCode === 38) {
if (activeSuggestion === 0) {
return;
}
this.setState({ activeSuggestion: activeSuggestion - 1 });
}
// User pressed the down arrow
else if (e.keyCode === 40) {
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
this.setState({ activeSuggestion: activeSuggestion + 1 });
}
};
render() {
const {
onChange,
onClick,
onKeyDown,
state: {
activeSuggestion,
filteredSuggestions,
showSuggestions,
userInput
}
} = this;
let suggestionsListComponent;
if (showSuggestions && userInput) {
if (filteredSuggestions.length) {
suggestionsListComponent = (
<ul class="suggestions">
{filteredSuggestions.map((suggestion, index) => {
let className;
// Flag the active suggestion with a class
if (index === activeSuggestion) {
className = "suggestion-active";
}
return (
<li className={className} key={suggestion} onClick={onClick}>
{suggestion}
</li>
);
})}
</ul>
);
} else {
suggestionsListComponent = (
<div class="no-suggestions">
<em>No suggestions, you're on your own!</em>
</div>
);
}
}
return (
<Fragment>
<input
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
value={userInput}
/>
{suggestionsListComponent}
</Fragment>
);
}
}
export default AutocompleteClass;
I want to give to my suggestor a value from a Json file. The name of those value are displayed in my select but when I am returning with an alert() my value it shows 'null'.
I want to use as a value the name already displayed/visible in the select.
The purpose of my application is after somebody choose a name in the select to show all the data from both database that has the same name.
This is the structure of both JSON file they all have different id, name,admin_area & country value
{
"id": 3,
"name": "Satimola",
"admin_area": "Theo",
"country": "Taiwan"
}
Of course I tried to pass my array suggestionOldData as a value but it didn't work the console said :
Failed prop type: Invalid prop value of type array supplied to Suggestor, expected string.
import React from "react";
import { Component } from "react";
import ReactDOM from "react-dom";
// IMPORT DATA FROM JSON FILE
import NewData from "../../api/data/cities.json";
import OldData from "../../api/data/historical.json";
// IMPORT PAGINATION FILE
import Pagination from "./Pagination.js";
// IMPORT MODULE TO CREATE INPUT SEARCH
import Suggestor from "ssuggestor";
// CREATE A COMPONENT THAT WILL SHOW THE NEW DATA IN A TABLE
class NewDataTable extends React.Component {
constructor() {
super();
this.state = {
NewData: NewData,
pageOfItems: []
};
this.onChangePage = this.onChangePage.bind(this);
this.handleChange = this.handleChange.bind(this);
}
onChangePage(pageOfItems) {
// update state with new page of items
this.setState({ pageOfItems: pageOfItems });
}
// GET INPUT VALUE , GET DATA WITH THE SAME NAME IN BOTH ARRAY, CONSOLE.LOG BOTH
handleChange(event) {
var val = document.getElementById(Suggestor);
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log("Old Data =>", consoleOldData);
console.log("New Data =>", consoleNewData);
}
render() {
// GET DATA.NAME FROM OldData
const suggesttionOldData = OldData.map(value => value.name);
return (
<div>
<form>
<Suggestor
id="Suggestor"
list={suggesttionOldData}
onSelect={this.handleChange}
placeholder=" ..."
value={this.state.value}
/>
</form>
<nav>
<Pagination
items={this.state.NewData}
onChangePage={this.onChangePage}
/>
</nav>
<table>
<tbody>
<tr>
<th>New Data</th>
</tr>
{this.state.pageOfItems.map(item => (
<tr key={item.id}>
<td key={item.id}>{item.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
export default NewDataTable;
This is the suggestor class
class Suggestor extends PureComponent {
constructor(props) {
super(props);
autoBind(this);
this.input = React.createRef();
this.state = {
filtered: this.filter(props.list, props.value, false),
value: props.value,
open: false,
index: 0
};
}
componentDidMount() {
document.addEventListener('click', this._onClick);
}
componentWillUnmount() {
document.removeEventListener('click', this._onClick);
}
_onClick(event) {
if (!this.input.current.parentNode.contains(event.target)) {
this.close();
}
}
componentWillReceiveProps(nextProps) {
let value = this.state.value;
if (nextProps.value !== this.props.value && nextProps.value !== value) {
value = nextProps.value;
}
this.setState({
filtered: this.filter(nextProps.list, value, true),
value
});
}
close() {
this.setState({
open: false,
filtered: this.unfilter(),
index: 0
});
}
handleClick() {
if (this.props.openOnClick) {
if (this.state.open) {
this.close();
} else {
this.setState({ open: true, filtered: this.unfilter() });
}
}
}
handleKeyDown(e) {
const { onKey, useKeys } = this.props;
onKey(e);
if (useKeys && this.processKey(e.keyCode)) {
e.preventDefault();
}
}
processKey(code) {
const { open, index, filtered, value } = this.state;
const ssuggestions = filtered.length ? filtered : this.unfilter();
let nextIndex;
switch (code) {
case keys.ENTER:
if (open && filtered[index]) {
this.changeValue(filtered[index].word, true);
} else {
this.setState({ open: true, filtered: this.unfilter() });
}
break;
case keys.ESCAPE:
this.close();
if (!open && value) {
this.changeValue('');
}
break;
case keys.DOWN:
nextIndex = (index + open) % ssuggestions.length;
break;
case keys.UP:
nextIndex = (index || ssuggestions.length) - 1;
break;
case keys.TAB:
if (this.props.selectOnTab && open && filtered[index]) {
this.changeValue(filtered[index].word, true);
} else {
this.close();
}
default:
return false;
}
if (nextIndex !== undefined) {
this.setState({ open: true, index: nextIndex, filtered: ssuggestions });
}
return true;
}
handleItemClick({ word }) {
this.changeValue(word, true);
}
handleItemMouseEnter(index) {
this.setState({ index });
}
handleChange(e) {
e.stopPropagation();
const value = e.target.value;
this.changeValue(value);
}
remove() {
this.changeValue('', true);
}
changeValue(value, select = false) {
const { list, suggestOn, accents, onChange, onSelect } = this.props;
const filtered = this.filter(list, value);
const suggest = value.length >= suggestOn;
const open = !!filtered.length && suggest;
this.setState({ value, filtered, open }, () => {
onChange(value);
if (select) {
const suggestion = filtered.find(({ word }) => transform(accents, word) === transform(accents, value));
onSelect(value, suggestion && suggestion.item);
this.close();
}
});
}
filter(list, value, onlyMatch = true) {
const { accents, selector } = this.props;
value = transform(accents, value);
let mapped = list.map(item => {
const word = selector(item);
return {
index: transform(accents, word).indexOf(value),
word,
item
};
});
if (onlyMatch) {
mapped = mapped.filter(item => item.index !== -1);
}
return mapped;
}
unfilter() {
return this.filter(this.props.list, this.state.value, false);
}
focus() {
this.input.current.focus();
}
render() {
const { theme, style, placeholder, arrow, close, tooltip, required } = this.props;
const { open, value, index, filtered } = this.state;
const displaySuggestions = open && !!filtered.length;
return (
<div className={theme.root} onClick={this.handleClick} onKeyDown={this.handleKeyDown} style={style}>
<input
type="text"
className={theme.input}
onChange={this.handleChange}
value={value}
title={tooltip}
placeholder={placeholder}
required={required}
ref={this.input}
/>
{arrow && <span className={theme.arrow} />}
{close && value && <span className={theme.close} onClick={this.remove} />}
{displaySuggestions && (
<ul className={theme.list}>
{filtered.map((item, i) => (
<ListItem
key={item.word}
theme={theme}
item={item}
index={i}
onItemClick={this.handleItemClick}
onItemMouseEnter={this.handleItemMouseEnter}
overItem={i === index}
search={value}
/>
))}
</ul>
)}
</div>
);
}
}
Suggestor.propTypes = {
list: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object])).isRequired,
selector: PropTypes.func,
onChange: PropTypes.func,
onSelect: PropTypes.func,
onKey: PropTypes.func,
value: PropTypes.string,
openOnClick: PropTypes.bool,
selectOnTab: PropTypes.bool,
placeholder: PropTypes.string,
tooltip: PropTypes.string,
theme: PropTypes.shape({
root: PropTypes.string,
arrow: PropTypes.string,
close: PropTypes.string,
list: PropTypes.string,
item: PropTypes.string,
activeItem: PropTypes.string
}),
suggestOn: PropTypes.number,
style: PropTypes.object,
required: PropTypes.bool,
useKeys: PropTypes.bool,
accents: PropTypes.bool,
arrow: PropTypes.bool,
close: PropTypes.bool
};
Suggestor.defaultProps = {
theme: {},
selector: s => s,
onSelect: noop,
onChange: noop,
onKey: noop,
value: '',
openOnClick: true,
selectOnTab: false,
suggestOn: 1,
required: false,
accents: false,
useKeys: true,
arrow: true,
close: true
};
export default Suggestor;
So, looking at the example mentioned in the readme for ssugester package, it looks like you are passing wrong callback for the onSelect method.
So, I believe changing your code to the following way should work.
Change the Suggester component to have a function that represents the onSelect callback in a more meaningful way, for example:
<Suggestor
id;= "Suggestor";
list = {suggesttionOldData};
onSelect = {this.onSuggesterSelect};
placeholder =' ...'
value = {this.state.value}
/ >
and in your NewDataTable component, create another function called onSuggesterSelect.
// GET INPUT VALUE , GET DATA WITH THE SAME NAME IN BOTH ARRAY, CONSOLE.LOG BOTH
onSuggesterSelect(value, suggestion) {
const val = value; // this should be your selected value (name) now.
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log('Old Data =>', consoleOldData);
console.log('New Data =>', consoleNewData);
}
So, with these changes your component should look something like this:
import React from "react";
import { Component } from "react";
import ReactDOM from "react-dom";
// IMPORT DATA FROM JSON FILE
import NewData from "../../api/data/cities.json";
import OldData from "../../api/data/historical.json";
// IMPORT PAGINATION FILE
import Pagination from "./Pagination.js";
// IMPORT MODULE TO CREATE INPUT SEARCH
import Suggestor from "ssuggestor";
// CREATE A COMPONENT THAT WILL SHOW THE NEW DATA IN A TABLE
class NewDataTable extends React.Component {
constructor() {
super();
this.state = {
NewData: NewData,
pageOfItems: []
};
this.onChangePage = this.onChangePage.bind(this);
this.handleChange = this.handleChange.bind(this);
}
onChangePage(pageOfItems) {
// update state with new page of items
this.setState({ pageOfItems: pageOfItems });
}
// GET INPUT VALUE , GET DATA WITH THE SAME NAME IN BOTH ARRAY, CONSOLE.LOG BOTH
handleChange(event) {
var val = document.getElementById(Suggestor);
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log("Old Data =>", consoleOldData);
console.log("New Data =>", consoleNewData);
}
onSuggesterSelect(value, suggestion) {
const val = value; // this should be your selected value (name) now.
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log('Old Data =>', consoleOldData);
console.log('New Data =>', consoleNewData);
}
render() {
// GET DATA.NAME FROM OldData
const suggesttionOldData = OldData.map(value => value.name);
return (
<div>
<form>
<Suggestor
id="Suggestor"
list={suggesttionOldData}
onSelect={this.onSuggesterSelect}
placeholder=" ..."
value={this.state.value}
/>
</form>
<nav>
<Pagination
items={this.state.NewData}
onChangePage={this.onChangePage}
/>
</nav>
<table>
<tbody>
<tr>
<th>New Data</th>
</tr>
{this.state.pageOfItems.map(item => (
<tr key={item.id}>
<td key={item.id}>{item.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
export default NewDataTable;
I hope this works for you now.
var val = document.getElementById(Suggestor);
Change to
var val = event.target.value
The component must be part of the actions column and be rendered for the "workflow" type
The component should be able to render only a button, which when clicked starts the workflow configured in the action, OR a dropdown with different options which when clicked start the workflow with the clicked option as the workflow arguments
The component should use the connectWorkflow decorator, which adds different props for interacting with the workflows API, e.g. startFlow, resumeFlow. The functions and their arguments can be seen in the WorkflowManager class
When the user clicks the button or an option the component should call the startFlow function from the props, with the workflowPath configured in the action
The component should be able to pass input data to the workflow, that is retrieved from the specific table row data. It should be able to accept an option in the action definition in the ListPage columns prop, that is an Object which will be passed as the input data to the startFlow function. Before being passed any key or value from this object should be checked if there are some values in them that should be replaced with the table row's data
type Props = {
workflowPath: string;
executionId: string,
data: Object,
actionHandlers: {
[string]: {
async: boolean,
func: (data: { executionId: string, [string]: any }, context: Object) => any,
},
},
startFlow: Function,
resumeFlow: Function,
};
type State = {
workflowCode: string,
executionId: string,
loading: boolean,
}
#connectWorkflow
class Workflow extends React.Component<Props, State> {
static defaultProps = {
executionId: '',
data: {},
actionHandlers: {},
startFlow: () => undefined,
resumeFlow: () => undefined,
};
state = {
workflowCode: '',
executionId: '',
loading: true,
};
componentDidMount() {
const {
workflowPath, executionId, startFlow, resumeFlow, data, actionHandlers,
} = this.props;
if (executionId) {
resumeFlow(executionId, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
} else {
startFlow(workflowPath, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
}
}
componentDidUpdate(prevProps: Props) {
const {
workflowPath, executionId, startFlow, resumeFlow, data, actionHandlers,
} = this.props;
if (prevProps.workflowPath !== workflowPath) {
if (executionId) {
resumeFlow(executionId, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
} else {
startFlow(workflowPath, data, actionHandlers).then(({ id: execId, workflow_name: workflowCode }) => {
this.setState({ executionId: execId, workflowCode, loading: false });
});
}
}
}
render() {
const { executionId: executionIdProps } = this.props;
const { executionId, loading, workflowCode } = this.state;
// TODO: i18n
return (
<React.Fragment>
<WorkflowForm
workflowCode={workflowCode}
executionId={executionIdProps || executionId}
/>
{loading && (
<Layer margin="medium" plain>
<Box>
<Text>Loading</Text>
</Box>
</Layer>
)}
</React.Fragment>
);
}
}
export default Workflow;
Then I have error here: Super expression must either be null or a function
// #flow
import * as React from 'react';
import { Box, Button } from 'grommet';
import { Launch } from 'grommet-icons';
import connectWorkflow from '../../../../../../../../src/components/workflows/connectWorkflow';
type Props = {
startFlow: Function,
}
#connectWorkflow
class WorkflowComponent extends React.ComponentType<Props> {
static defaultProps = {
startFlow: () => {
},
};
handleStart = () => {
this.props.startFlow();
};
render() {
return (
<Box>
<Button
label="Star Flow"
position="right"
icon={<Launch />}
onClick={this.handleStart}
/>
</Box>
);
}
}
export default WorkflowComponent;
The error means that parent class is not valid class but something else.
React.ComponentType is a type, not a class. It doesn't exist at run time, another class cannot extend it. WorkflowComponent should extend React.Component. With types it likely should be:
class WorkflowComponent extends React.Component<Props> {...}
At present I have a ValidatedTextField component that wraps a TextField component and takes in a validationerror property that is used to communicate between the child and parent and consumed by either the onChange or onBlur event of the textbox.
However when passing a function to this attribute I receive the following error:
Invalid value for prop validationerror
on tag. Either remove it from the element, or pass a string or number value to keep
it in the DOM. For details,
see https://reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html#changes-in-detail
I have read through the link which says that data and aria attributes can still be passed freely, however switching to using data attribute results in the same error. I cannot think how else to send the function to update the parents error back.
From the wrapper
<ValidatedTextField
type="text"
variant="standard"
required={true}
validateon={'onChange'}
validate={[Validations.Required, Validations.allowedNameCharacters]}
validationerror={(validationError: boolean) => this.setState({ HasError: validationError }) }
onChange={(event: any) => this.setState({ textboxvalue: event.target.value })}
value={this.state.textboxvalue}
/>
and the wrapped component
import * as React from 'react';
import * as _ from 'lodash'
import { IValidationItem } from '../../../Interfaces/IValidationItem'
import TextField, { TextFieldProps } from "#material-ui/core/TextField";
interface IValidatedTextFieldProps {
validate?: IValidationItem[],
validateon?: 'onBlur' | 'onChange',
validationerror?: (hasError?: boolean) => void
}
interface IValidatedTextFieldState {
validationErrorMessage: string,
validationError: boolean
}
type ValidatedTextFieldAllProps = IValidatedTextFieldProps & TextFieldProps
class ValidatedTextField extends React.Component<ValidatedTextFieldAllProps, IValidatedTextFieldState> {
public constructor(props: ValidatedTextFieldAllProps) {
super(props);
this.state = {
validationErrorMessage: "",
validationError: false
}
}
public validationWrapper = (event: any) => {
const { validate, } = this.props;
return !validate ? "" : _.forEach(validate, (validationItem: IValidationItem) => {
const result = !validationItem.func(event.target.value)
if (result) {
this.setState({ validationErrorMessage: validationItem.validationMessage });
this.setState({ validationError: result })
this.callParentValidationErrorMethod(result)
return false;
}
else {
this.setState({ validationErrorMessage: "" });
this.setState({ validationError: result })
this.callParentValidationErrorMethod(result)
return;
}
});
};
public onBlurValidation = (event: any) => {
const { onBlur, validateon, validate } = this.props;
if (_.isFunction(onBlur)) { onBlur(event); }
if (validateon === "onBlur" && !!validate) { this.validationWrapper(event);
}
public onChangeValidation = (event: any) => {
const { onChange, validateon, validate } = this.props;
if (_.isFunction(onChange)) { onChange(event); }
if (validateon === "onChange" && !!validate) { this.validationWrapper(event); };
}
public callParentValidationErrorMethod = (hasError: boolean) => {
if(_.isFunction(this.props.validationerror)) {
this.props.validationerror(hasError);
}
}
public render() {
const { validationErrorMessage, validationError } = this.state
return (<TextField
{...this.props}
onBlur={(event: any) => { this.onBlurValidation(event); }}
onChange={(event: any) => { this.onChangeValidation(event); }
}
error={validationError}
helperText={validationErrorMessage}
/>)
}
}
export default ValidatedTextField;
Additional info: Not seen in IE only chrome so far and currently React v16.6
Alright Solved
Issue was that I was spreading all properties on the textfield in the component including the non existent attributes on the textfield.
Extrapolating the properties I did not need and only binding the default text field properties fixed this issue
const {validationerror,validate,validateon, ...textFieldProps } = this.props;
return (<TextField
{...textFieldProps}
onBlur={(event: any) => { this.onBlurValidation(event); }}
onChange={(event: any) => { this.onChangeValidation(event); }
}
error={validationError}
helperText={validationErrorMessage}
/>)
import React, { Component } from 'react';
import DisplayTable from './Table.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuItems: this.props.menu_items,
searchString: '',
displayItems: this.props.menu_items
}
this.search = this.search.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentWillMount() {
this.props.get_menu_items_api(false);
}
componentWillReceiveProps(nextProps) {
this.setState({ menuItems: nextProps.menu_items })
}
handleChange(e, isEnter) {
const searchData = () => {
let tempMenuProductDetails = this.props.menu_items;
const filterArray = tempMenuProductDetails.reduce((result, category) => {
if (category.categoryName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1) {
result.push(category);
}
if (category.productList && category.productList.length > 0) {
category.productList = category.productList.reduce((productListResult,
productList) => {
if (!!productList.productName &&
productList.productName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1)
{
productListResult.push(productList);
}
return productListResult;
}, []);
}
return result;
}, []);
this.setState({
displayItems: filterArray
}, function () {
console.log(this.state.displayItems);
})
console.log(filterArray);
}
if (!isEnter) {
this.setState({
searchString: e.target.value
});
} else {
searchData();
}
}
search(e) {
if (e.keyCode == 13) {
this.handleChange(e, true);
}
this.handleChange(e, false);
}
render() {
console.log(this.state.displayItems);
console.log(this.props.menu_items);
console.log(this.state.menuItems);
return (
<DisplayTable dataProp={this.state.displayItems} editFuncProp=
{this.props.edit_menu_items_api} /> )
}
}
export default App;
I have this search function in this file that does not update the value of props coming from the container of redux. Now when I pass {this.state.displayItems} in menu ,it does not display the data.
But when I pass {this.props.menu_items} it displays the data and I am not able to modify this.props.menu_items on the basis of search.
I have tried this code . what should i do?
The problem seems to be that, initially this.props.menu_items is an empty array and only after some API call the value is updated and you get the returned array on the second render, thus if you use it like
<DisplayTable dataProp={this.props.menu_items} editFuncProp=
{this.props.edit_menu_items_api} />
it works. Now that you use
<DisplayTable dataProp={this.state.displayItems} editFuncProp=
{this.props.edit_menu_items_api} />
and displayItems is only initialized in the constructor which is only executed once at the time, component is mounted and hence nothing is getting displayed.
The solution seems to be that you update the displayItems state in componentWillReceiveProps and call the search function again with the current search string so that you search results are getting updated.
Code:
import React, { Component } from 'react';
import DisplayTable from './Table.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuItems: this.props.menu_items,
searchString: '',
displayItems: this.props.menu_items
}
this.search = this.search.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentWillMount() {
this.props.get_menu_items_api(false);
}
componentWillReceiveProps(nextProps) {
this.setState({ menuItems: nextProps.menu_items, displayItems: nextProps.menu_items })
this.handleChange(null, true);
}
handleChange(e, isEnter) {
const searchData = () => {
let tempMenuProductDetails = this.props.menu_items;
const filterArray = tempMenuProductDetails.reduce((result, category) => {
if (category.categoryName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1) {
result.push(category);
}
if (category.productList && category.productList.length > 0) {
category.productList = category.productList.reduce((productListResult,
productList) => {
if (!!productList.productName &&
productList.productName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1)
{
productListResult.push(productList);
}
return productListResult;
}, []);
}
return result;
}, []);
this.setState({
displayItems: filterArray
}, function () {
console.log(this.state.displayItems);
})
console.log(filterArray);
}
if (!isEnter) {
this.setState({
searchString: e.target.value
});
} else {
searchData();
}
}
search(e) {
if (e.keyCode == 13) {
this.handleChange(e, true);
}
this.handleChange(e, false);
}
render() {
console.log(this.state.displayItems);
console.log(this.props.menu_items);
console.log(this.state.menuItems);
return (
<DisplayTable dataProp={this.state.displayItems} editFuncProp=
{this.props.edit_menu_items_api} /> )
}
}
export default App;