---
tags: ironhack, lecture,
---
<style>
.markdown-body img[src$=".png"] {background-color:transparent;}
.alert-info.lecture,
.alert-success.lecture,
.alert-warning.lecture,
.alert-danger.lecture {
box-shadow:0 0 0 .5em rgba(64, 96, 85, 0.4); margin-top:20px;margin-bottom:20px;
position:relative;
ddisplay:none;
}
.alert-info.lecture:before,
.alert-success.lecture:before,
.alert-warning.lecture:before,
.alert-danger.lecture:before {
content:"👨🏫\A"; white-space:pre-line;
display:block;margin-bottom:.5em;
/*position:absolute; right:0; top:0;
margin:3px;margin-right:7px;*/
}
b {
--color:yellow;
font-weight:500;
background:var(--color); box-shadow:0 0 0 .35em var(--color),0 0 0 .35em;
}
.skip {
opacity:.4;
}
</style>

# React | Forms
## Learning Goals
After this lesson, you will be able to:
- Create forms in your React apps
- Understand how to control the components where forms are displayed
- Learn how to get the value of different types of inputs
## Introduction
:::info lecture
Les formulaires sont un moyen de discussion avec nos utilisateurs !
:::
:::info lecture
Les champs de formulaires contiennent de l'information. On peut également parler d'état d'un champ de formulaire : par ex, pour une case à cocher :
<span style="font-size:300%">⚪️/🔘</span>
:::
<div style="display:flex; justify-content:center">
<img src="https://user-images.githubusercontent.com/23629340/40714028-c48b7f7c-6401-11e8-84a4-8e53398c9458.png" style="height: 400px">
</div>
In this learning unit we will see how to implement **forms** in our React apps. As we already know, `HTML form` elements work a little bit differently from other **DOM** elements, because form elements naturally keep some *internal state*.
## Forms
:::info lecture
Par default les `<form>` rechargent la page vers l'URL de l'`action=""`.
:::
`<form>` tag has a default behavior of redirecting to a new page when the user `submits` the form. In case we want that behavior in our React App, it will still work, but this makes no sense for our *single page* apps.
:::info lecture
Pas de sens dans une SPA.
On veut prévenir ca :
```jsx
<form onSubmit={this.handleSubmit}>
```
```jsx
handleSubmit(event) {
event.preventDefault();
}
```
:::
That's why in most cases, it’s convenient to have a JavaScript function that handles the submission of the form and has access to the data that the user entered into the form.
**The standard way to achieve this is with a technique called “controlled components”.**
### Controlled Components
:::info lecture
En react, on veut que tout parte des state. On veut que ca soit l'update d'un state qui controle le rendu : **single source of truth**
:::
In HTML, form elements such as `<input>`, `<textarea>`, and `<select>` typically maintain their own state and update it based on user input. **In React**, mutable state is typically kept in the state property of components, and **only updated with `setState()`**.
We can combine the two by making the React **state** be the **“single source of truth”**. Then the React component that renders a form also controls what happens in that form on subsequent user input. An `input` form element whose value is controlled by React in this way is called a “controlled component”.
Remember how we asked you to add Harry Potter movie in one of the previous lessons? What we really wanted you to do is pretty much to hard code object that has some properties and its values and to use *push()* or *unshift()* method so you can add it into the array of movies that lives in the state of `<DynamicMoviesList />`. So let's now do the same but let's make it dynamic so you can add any move you want and as many movies as you want :smiley: .
The first thing we will do is create new `AddMovie.js` file inside our `components/dynamicListsDemo` folder. This file will hold stateful `<AddMovie />` component and its `constructor()` will have state set to properties of the movie object that will be created inside this component. Let's do it:
:::info lecture
Créons un nouveau composant `<AddMovie>` dans `components/dynamicListsDemo/` :
:::
```jsx
// components/dynamicListsDemo/AddMovie.js
import React, { Component } from 'react';
class AddMovie extends Component {
constructor(props){
super(props);
this.state = {
title: '',
director: '',
hasOscars: false,
IMDbRating: ''
}
}
render(){
return (
// form will be added here!
)
}
}
export default AddMovie;
```
:::info lecture
Remarquons les `state` qui vont être utilisés pour le rendu du formulaire.
NB : remarquons ici l'utilisation du `constructor` au lieu de les définir directement dans la `class`.
:::
Good, so far we have the state set so let's move to creating our first React form:
:::info lecture
Et maintenant le markup :
:::
```jsx
// components/dynamicListsDemo/AddMovie.js
...
render(){
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>Title:</label>
<input type="text" name="title" value={this.state.title} />
<label>Director:</label>
<input type="text" name="director" value={this.state.director} />
<label>Oscar Awarded:</label>
<input type="checkbox" name="hasOscars" checked={this.state.hasOscars} />
<label>IMDb Rating:</label>
<input type="text" name="IMDbRating" value={this.state.IMDbRating} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
...
```
:::info lecture
Notons ici que chaque state est utilisé pour chaque champ du formulaire :
```jsx
value={this.state.XXX}
```
:::
We still can't see the form because we didn't use this component yet. So let's go and import it into our `DynamicMoviesList.js` and let's place it into the DOM.
:::info lecture
Instancions notre composant `<AddMovie>` dans `DynamicMoviesList` :
:::
```jsx
// components/dynamicListsDemo/DynamicMoviesList.js
...
import AddMovie from './AddMovie'; // 👈
class DynamicMoviesList extends Component {
...
render() {
return (
<div>
<AddMovie /> {/* 👈 */}
...
</div>
)
}
}
export default DynamicMoviesList;
```
Okay, now we can see the form BUT the browser's console is on fire :fire: :rotating_light: . We have to do something with this error:
:::danger
*You provided a `value` prop to a form field without an `onChange` handler... set either `onChange` or `readOnly`.*
:::
:::danger
*You provided a `checked` prop to a form field without an `onChange` handler... set either `onChange` or `readOnly`.*
:::
:::info lecture
Plein de warnings en console !

:::
Basically what is going on is that the state is not being updated and the *value* inside the *input* is setting the input all the time to the values from the current state. By default, they are looking for some **change** in the state to happen and since that is not happening we are seeing all these errors. So we have to `handle the changes` somehow and the best way to do it is by using `onChange` listener. `handleChange` runs on every `keystroke` to update the React state, the displayed value will update as the user types.
With a **controlled component**, every state mutation will have an associated handler function. This makes it straightforward to modify or validate user `input`.
Let's then fix this error in our code:
:::info lecture
Corrigeons cela en ajoutant à chacun des champs un attribut `onchange` :
:::
```jsx
// components/dynamicListsDemo/AddMovie.js
import React, { Component } from 'react';
class AddMovie extends Component {
constructor(props) {
...
}
handleTitleInput = (event) => {
this.setState({
title: event.target.value
})
}
handleDirectorInput = (event) => {
this.setState({
director: event.target.value
})
}
handleHasOscarsCheck = (event) => {
this.setState({
hasOscars: event.target.type=== 'checkbox' ? event.target.checked : event.target.value
})
}
handleRating = (event) => {
this.setState({
IMDbRating: event.target.value
})
}
render(){
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>Title:</label>
<input type="text" name="title" value={this.state.title} onChange={(e) => this.handleTitleInput(e)} />
<label>Director:</label>
<input type="text" name="director" value={this.state.director} onChange={(e) => this.handleDirectorInput(e)} />
<label>Oscar Awarded:</label>
<input type="checkbox" name="hasOscars" checked={this.state.hasOscars} onChange={(e) => this.handleHasOscarsCheck(e)} />
<label>IMDb Rating:</label>
<input type="text" name="IMDbRating" value={this.state.IMDbRating} onChange={(e) => this.handleRating(e)} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
export default AddMovie;
```
:::info lecture
Maintenant, on peut taper dans le champ et les state sont bien mis a jour en fonction :

Bien expliquer le flow : change -> setState -> render
:::
---
:::info lecture
Comment maintenant **ajouter un nouveau film** à la liste `<DynamicMoviesList>.state.movies` ?
:::
:::danger lecture
`AddMovie` ne dispose pas de la liste des films ! C'est `DynamicMoviesList` qui les porte...
:::
Okay, great, the error is gone but let's see what should be our next step. The *movies* array we have to update is in `<DynamicMoviesList />` component. What do we have in common between these two components? Well, we have `<AddMovie />` as tag inside *render()* method of `<DynamicMoviesList />`. *DynamicMoviesList* component also has *addMovieHandler()* method that we will eventually use to update *movies* array. Logical question here is how to pass this method into "child" component (*AddMovie*), and we already know the answer, right? We learned that we use `props` to pass anything down to components, and we will use our knowledge here once more.
:::info lecture
Nous allons pour cela, "passer" au composant `<AddMovie>` une fonction (qu'il nous restera à écrire) capable elle d'ajouter un film :
:::
```jsx
// components/dynamicListsDemo/DynamicMoviesList.js
...
render() {
return (
<div>
<AddMovie addTheMovie={this.addMovieHandler} />
{/* 👆 👆*/}
...
</div>
)
}
...
```
:::info lecture
On passe au composant `<AddMovie>` une prop `addTheMovie` faisant référence à une méthode du composant parent que l'enfant pourra appeler pour mettre à jour la liste des films de `<DynamicMoviesList>.state.movies` **à laquelle il n'a pas directement accès** (cf. ligne 14 ci-après).
:::
This means that we can use `addTheMovie` as props inside the `<AddMovie />` component. The best place to make use of it inside `handleFormSubmit()` method because this method gets executed *onSubmit*, so whenever we are ready to push the new movie into the movies array.
:::info lecture
**Dans l'enfant `AddMovie`**, voici comment, à la soumission du formulaire d'ajout d'un film, nous appèlerons la fonction passée :
:::
```jsx=
// components/dynamicListsDemo/AddMovie.js
...
class AddMovie extends Component {
...
// 👇
handleFormSubmit = (event) => {
event.preventDefault(); // Prevent page refresh
// Call the parent the new movie datas
this.props.addTheMovie(this.state); {/* 👈 */}
// RESET
this.setState({
title: '',
director: '',
hasOscars: false,
IMDbRating: ''
})
}
render(){
...
}
}
export default AddMovie;
```
:::info lecture
- ligne 11 : on empêche la soumission du formulaire et le rafraichissement de la page
- ligne 14 : on fait appel à la fonction qu'on nous à demandé d'appeler quand on désire rajouter un film à la liste du parent (**`this.state` contient toutes les données saisies du formulaire**).
- ligne 17 à 22 : on remet le formulaire a 0
:::
We are on our last step: we see that `this.props.addTheMovie(this.state)` receives argument (the new movie object) so let's update our original, the `addTheMovie()` method inside `<DynamicMoviesList />`.
:::info lecture
Côté parent, ne nous reste plus qu'à écrire cette fameuse fonction capable d'ajouter un film à la liste `movies` des `state` :
:::
```jsx
// components/dynamicListsDemo/DynamicMoviesList.js
...
addMovieHandler = (theMovie) => {
const moviesCopy = [...this.state.movies]; // copy!
moviesCopy.push(theMovie); // add
this.setState({
movies: moviesCopy
})
}
...
```
:::info lecture
Dans le parent, `this.state.movies` est mis à jour avec un nouveau film transmis par l'enfant `<AddMovie>`
<span style="font-size:300%">🙌</span>
:::
#### Handling multiple inputs
Works perfectly but still our code is not really *DRY*. We have four handlers for four inputs. That is quite okay but there's way to *handle multiple inputs* all at the same time. If we comment out all four handlers and add new one (*handleChange()*), we should see the same result. The final version of our our `<AddMovie />` component is this:
:::info lecture
Rendons notre code un peu plus DRY, avec une seule et même fonction `handleChange` valable pour tous les changements de champs :
:::
```jsx=
// components/dynamicListsDemo/AddMovie.js
class AddMovie extends Component {
constructor(props){
...
}
handleFormSubmit = (event) => {
...
}
// 👇
handleChange = (event) => {
let { name, value, type } = event.target;
// special case for checkboxes (where value is hold in checked property)
if (type === 'checkbox') {
value = event.target.checked;
}
// set the corresponding state to the input's value
this.setState({[name]: value});
}
render(){
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>Title:</label>
<input type="text" name="title" value={this.state.title} onChange={(e) => this.handleChange(e)} />
<label>Director:</label>
<input type="text" name="director" value={this.state.director} onChange={(e) => this.handleChange(e)} />
<label>Oscar Awarded:</label>
<input type="checkbox" name="hasOscars" checked={this.state.hasOscars} onChange={(e) => this.handleChange(e)} />
<label>IMDb Rating:</label>
<input type="text" name="IMDbRating" value={this.state.IMDbRating} onChange={(e) => this.handleChange(e)} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
export default AddMovie;
```
:::success lecture
`event.target` est une référence vers l'élément HTML d'origine duquel l'évènement est parti (cf. https://slides.com/abernier/ifocop298#/0/229), ie : l'`input` qui vient de `onChange`r 😉
:::
:::info lecture
Nous créons une fonction générique pour n'importe quel champ.
- ligne 14 : on récupère `name` et `value` depuis le champ concerné
- ligne 16 : traitement spécial pour les checkbox afin de leur permettre d'etre un boolean
- ligne 22 : `.setState` avec la syntaxe `{[name]: value}` équivalente à:
```jsx
let o = {};
o[name] = value;
this.setState(o);
```
:::
Now our code looks pretty clean.
In case you still miss some basic concepts, simple example of using input inside forms is given here also:
<iframe height='322' scrolling='no' title='Controlled Text Example' src='//codepen.io/ironhack/embed/zaxvzr/?height=322&theme-id=0&default-tab=js,result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ironhack/pen/zaxvzr/'>Controlled Text Example</a> by Ironhack (<a href='https://codepen.io/ironhack'>@ironhack</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>
#### The textarea and select tag
:::info lecture
pareil
:::
The last part of this lesson we will dedicate to explain how `<textarea>` and `<select>` elements work and we will make it easier by saying that they work the same way as `<input>` element.
In order to demonstrate its functionality, let's create one more component inside `components/dynamicListsDemo` folder and let's name it `TextareaAndSelectTagDemo.js`.
This will be stateful component and it will explain how to use these tags.
```jsx=
// components/dynamicListsDemo/TextareaAndSelectTagDemo.js
import React, { Component } from 'react';
class TextareaAndSelectTagDemo extends Component {
constructor(props) {
super(props);
this.state = {
selectInputValue: "Notebook",
textareaInputValue: "..."
}
}
handleChange = (event) => {
const { name, value } = event.target;
this.setState({[name]: value});
}
handleSubmit = (event) => {
event.preventDefault();
alert(this.state.selectInputValue + ' <==> ' + this.state.textareaInputValue);
}
render(){
return(
<form onSubmit={this.handleSubmit}>
<label> Pick your favorite movie: </label>
<select name="selectInputValue" value={this.state.selectInputValue} onChange={e => this.handleChange(e)} >
<option value={this.state.selectInputValue}>{this.state.selectInputValue}</option>
<option value="Star Wars">Star Wars</option>
<option value="The Godfather">The Godfather</option>
<option value="Titanic">Titanic</option>
<option value="Seven">Seven</option>
</select>
<label> Why is this your favorite movie? </label>
<textarea name="textareaInputValue" value={this.state.textareaInputValue} onChange={e => this.handleChange(e)} />
<input type="submit" value="Submit" />
</form>
)
}
}
export default TextareaAndSelectTagDemo;
```
To see how this works, we have to use `<TextareaAndSelectTagDemo />`component. Let's import it in *App.js* and use it in its *render()* method.
```jsx
// App.js
import React, { Component } from 'react';
import './App.css';
import Header from './components/Header.js'
import { listItems, MoviesList } from './components/ListDemo';
import DynamicMoviesList from './components/dynamicListsDemo/DynamicMoviesList';
// 👇 ADD
import TextareaAndSelectTagDemo from './components/dynamicListsDemo/TextareaAndSelectTagDemo';
class App extends Component {
render() {
return (
<div className="App">
<Header />
<ul className="list-style">{ listItems }</ul>
<div>
{/* {MoviesList()} */}
<MoviesList />
</div>
<hr />
<DynamicMoviesList />
<TextareaAndSelectTagDemo /> {/* 👈 ADD */}
</div>
);
}
}
export default App;
```
## Summary
Controlling forms could be tricky in React, because elements naturally keep some internal state. On this learning unit we learned how to manage forms in our React app.
:::warning lecture
Pour les forms en react on oublie pas de :
- `event.preventDefault` sur le `onSubmit`
- `value={this.state.XXX}` et `onChange={this.handleChange}` sur chaque champ
:::
## Extra Resources
- [Forms](https://reactjs.org/docs/forms.html)