Reputation: 3841
I have a simple react-component which contains of a form
. In this form
the user has an search-box where to find users.
In order to have a valid form, there must be at least 3 users but not more than 6.
Therefore I've added a hidden field (hidden by surrounding display:none; div) which contains the number of already added users. This one will show the error-message in case the current number is invalid.
The following code is simplified but shows the main issue I have.
In my render-call I have:
render():JSX.Element {
return (
<form>
<ValidatableInput
input={<Input type="number" value={this.state.users.length.toString()} min={3} max={6} getRef={(el: HTMLInputElement) => this.testElement = el} onChange={() => alert("changed...")} />}
minValueMessage={"Currently there are " + this.state.users.length + " users. A minimum of 3 is needed"}
hidden={true} />
<UserSearch onUserSelected={this.handleUserSelected} />
// ...
</form>
);
}
whereas UserSearch
is the component to search for users. This is mainly an autocomplete-input which triggers the handleUserSelected
:
private handleUserSelected = (selectedElement: IUser) : void => {
// get a copy of the current state
const newState = { ...this.state };
// add the user
newState.users.push(selectedElement);
// thats what I've tried so far
this.testElement.dispatchEvent(new Event("change"));
this.testElement.dispatchEvent(new Event("change", {bubbles: true}));
this.testElement.onchange(new Event("change"));
this.testElement.onchange(new Event("change"), {bubbles: true});
this.setState(newState, () => // here I want to do the triggering as the input has now the new count as value set)
}
However, the console does not show changed
at all.
How can I call/trigger the change-event manually when something other changed on the component?
This is the ValidatableInput-component:
export class ValidatableInput extends React.Component<IValidatableInputProps, IValidatableInputState> {
render(): JSX.Element {
const { labelText, input, requiredMessage, maxLengthMessage, minValueMessage, hidden } = this.props;
input.props.onInvalid = this.handleInvalid;
input.props.onChange = this.handleChange;
if (hidden) {
// set height to 0
}
return (
<FormGroup row >
<Label for={input.props.name} md={3}>{!hidden && labelText}</Label>
<Col md={9}>
<div>
{input}
{this.state.validityState.valueMissing && <div className={classNames("invalid-feedback")}>{requiredMessage || "This field is required"}</div>}
{this.state.validityState.tooLong && <div className={classNames("invalid-feedback")}>{maxLengthMessage || "The field shoud not be longer than " + input.props.maxLength}</div>}
{this.state.validityState.rangeOverflow && <div className={classNames("invalid-feedback")}>{maxLengthMessage || "There are too many items. Maximum allowed: " + input.props.max}</div>}
{this.state.validityState.rangeUnderflow && <div className={classNames("invalid-feedback")}>{minValueMessage || "There are too less items. Minimum needed: " + input.props.min}</div>}
</div>
</Col>
</FormGroup>
);
}
private handleInvalid = (ev: React.FormEvent<HTMLInputElement>): any => {
const input = ev.target as HTMLInputElement;
this.setState({ validityState: input.validity });
}
private handleChange = (ev: React.FormEvent<HTMLInputElement>): any => {
const input = ev.target as HTMLInputElement;
this.setState({ validityState: input.validity });
}
}
Upvotes: 1
Views: 773
Reputation: 7424
Maybe keep it simpler and remove the refs and eventHandling boilerplate.
Your business logic relies on this.state.users.length
, right?
So use it on your favor:
handleUserSelected = (selectedElement: IUser) : void => {
this.setState({
users: [
...this.state.users,
selectedElement,
],
})
}
render() {
const { users } = this.state
const isInvalid = users.length < 3 || users.length > 6
return (
<form>
{isInvalid && <span>Your invalid message</span>}
<UserSearch ... />
</form>
)
}
Upvotes: 1