Je cherche à créer un composant sans état dont l'entrée peut être validée par l'élément parent.
Dans mon exemple ci-dessous, je rencontre un problème où la référence d'entrée n'est jamais affectée à la propriété privée _emailAddress
du parent.
Lorsque handleSubmit
est appelé, this._emailAddress
est undefined
. Y a-t-il quelque chose qui me manque ou y a-t-il une meilleure façon de le faire?
interface FormTestState {
errors: string;
}
class FormTest extends React.Component<void, FormTestState> {
componentWillMount() {
this.setState({ errors: '' });
}
render(): JSX.Element {
return (
<main role='main' className='about_us'>
<form onSubmit={this._handleSubmit.bind(this)}>
<TextInput
label='email'
inputName='txtInput'
ariaLabel='email'
validation={this.state.errors}
ref={r => this._emailAddress = r}
/>
<button type='submit'>submit</button>
</form>
</main>
);
}
private _emailAddress: HTMLInputElement;
private _handleSubmit(event: Event): void {
event.preventDefault();
// this._emailAddress is undefined
if (!Validators.isEmail(this._emailAddress.value)) {
this.setState({ errors: 'Please enter an email address.' });
} else {
this.setState({ errors: 'All Good.' });
}
}
}
const TextInput = ({ label, inputName, ariaLabel, validation, ref }: { label: string; inputName: string; ariaLabel: string; validation?: string; ref: (ref: HTMLInputElement) => void }) => (
<div>
<label htmlFor='txt_register_first_name'>
{ label }
</label>
<input type='text' id={inputName} name={inputName} className='input ' aria-label={ariaLabel} ref={ref} />
<div className='input_validation'>
<span>{validation}</span>
</div>
</div>
);
Vous ne pouvez pas accéder aux méthodes de type React (comme componentDidMount
, componentWillReceiveProps
, etc.) sur des composants sans état, y compris refs
. Checkout cette discussion sur GH pour la convo complète.
L'idée de apatride est qu'il n'y a pas d'instance créée pour elle (état). En tant que tel, vous ne pouvez pas attacher une ref
, car il n'y a aucun état auquel attacher la référence.
Votre meilleur choix serait de passer un rappel pour le moment où le composant change, puis d'affecter ce texte à l'état du parent.
Vous pouvez également renoncer au composant sans état et utiliser un composant de classe normal.
Vous ne pouvez pas utiliser l'attribut ref sur des composants fonctionnels car ils ne possèdent pas d'instances. Vous pouvez toutefois utiliser l'attribut ref à l'intérieur de la fonction de rendu d'un composant fonctionnel.
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
Vous pouvez utiliser le hook useRef
qui est disponible depuis v16.7.0-alpha
.
EDIT: Nous vous encourageons à utiliser Hooks en production à partir de la version 16.8.0
!
Les crochets vous permettent de maintenir l'état et de gérer les effets secondaires des composants fonctionnels.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
En savoir plus dans Documentation de l'API Hooks
La valeur de votre TextInput n'est rien d'autre qu'un état de votre composant. Ainsi, au lieu de récupérer la valeur actuelle avec une référence (mauvaise idée en général, autant que je sache), vous pouvez récupérer l'état actuel.
En version réduite (sans taper):
class Form extends React.Component {
constructor() {
this.state = { _emailAddress: '' };
this.updateEmailAddress = this.updateEmailAddress.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateEmailAddress(e) {
this.setState({ _emailAddress: e.target.value });
}
handleSubmit() {
console.log(this.state._emailAddress);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
value={this.state._emailAddress}
onChange={this.updateEmailAddress}
/>
</form>
);
}
}