Advanced TypeScript Programming Projects
上QQ阅读APP看书,第一时间看更新

Applying validation in a React component

In our mock layout, we identified that we wanted our validation to appear below the Save and Clear buttons. While we could do this inside our main component, we are going to separate our validation out into a separate validation component. The component will receive the current state of our main component, apply the validation whenever the state changes, and return whether we can save our data.

In a similar way to how we created our PersonalDetails component, we are going to create properties to pass into our component:

interface IValidationProps {
CurrentState : IPersonState;
CanSave : (canSave : boolean) => void;
}

We are going to create a component in FormValidation.tsx, that will apply the different IValidation classes that we have just created. The constructor simply adds the different validators into an array that we will shortly iterate over and apply the validation for:

export default class FormValidation extends React.Component<IValidationProps> {
private failures : string[];
private validation : IValidation[];

constructor(props : IValidationProps) {
super(props);
this.validation = new Array<IValidation>();
this.validation.push(new PersonValidation());
this.validation.push(new AddressValidation());
this.validation.push(new PhoneValidation());
}

private Validate() {
this.failures = new Array<string>();
this.validation.forEach(validation => {
validation.Validate(this.props.CurrentState, this.failures);
});

this.props.CanSave(this.failures.length === 0);
}
}

In the Validate method, we apply each piece of validation inside forEach before we call the CanSave method from our properties.

Before we add our render method, we are going to revisit PersonalDetails and add our FormValidation component:

<Row><FormValidation CurrentState={this.state} CanSave={this.userCanSave} /></Row>

The userCanSave method looks like this:

private userCanSave = (hasErrors : boolean) => {
this.canSave = hasErrors;
}

So, whenever the validation is updated, our Validate method calls back to userCanSave, which has been passed in as a property.

The last thing we need to do to get our validation running is to call the Validate method from the render method. We do this because the render cycle is called whenever the state of the parent changes. When we have a list of validation failures, we need to add them into our DOM as elements that we want to render back to the interface. A simple way to do this is to create a map of all of the failures and provide an iterator as a function that will loop over each failure and write it back as a row to the interface:

public render() {
this.Validate();
const errors = this.failures.map(function it(failure) {
return (<Row key={failure}><Col><label>{failure}</label></Col></Row>);
});
return (<Col>{errors}</Col>)
}

At this point, whenever we change the state inside the application, our validation will automatically be triggered and any failures will be written in the browser as a label tag.