You should be familiar with events if you've ever manipulated JavaScript. There's no harm in a quick reminder, though! An event is a reaction to an action carried out by the user, such as clicking a button or entering text in a form.
Some good news: with its practical and concise syntax, React makes managing events in the DOM much easier. 😉
Discover the React Events Syntax
When declaring an event in React, there are a few things to remember:
Events are written within a tag in
camelCase
.You declare the event to be captured, passing it the function to be called written between curly braces.
Unlike in JS, you’ll almost never have to use
addEventListener
.
Let’s try that out in our code. In components/PlantItem.js
, declare a handleClick
, which will make a log on our console:
function handleClick() {
console.log('✨ This is a click ✨')
}
Now add onClick={handleClick}
in the li
tag of the PlantItem
component, giving us:
<li className='jh-plant-item' onClick={handleClick}>
<img className='jh-plant-item-cover' src={cover} alt={`${name} cover`} />
{name}
<div>
<CareScale careType='water' scaleValue={water} />
<CareScale careType='light' scaleValue={light} />
</div>
</li>
Open the console and… Yay! It’s worked! 🥳
Let’s take this exercise a bit further by triggering an alert that displays the name of the plant that has been clicked on.
Pass plantName
as a parameter of handleClick
like this:
function handleClick(plantName) {
alert(`You want to buy 1 ${plantName}? Great choice 🌱✨`)
}
But if I click on it, it doesn’t work:
React passes an object by default (we’ll deal with this in a few minutes), but here you want to specify your argument.
It’s easy to do this: declare a function directly in onClick
(arrow functions are handy for doing this). This function will call handleClick, passing name
as a parameter:
onClick={() => handleClick(name)}
That should work perfectly! 😎
Use Synthetic Events
Let's see what it looks like when React passes the default object as a parameter to the functions indicated in the event callback:
If you pull the parameter in handleClick:
function handleClick(e) {
console.log('✨ This is my event :', e)
}
And then call it without passing the name:
onClick={handleClick}
Give it a go for yourself!
It looks like there’s a lot of information there – how does it all relate? 🤔
It is what’s known as a synthetic event. It’s the same interface as for native events in the DOM, except that these are compatible with all browsers.
Practical, isn’t it?
Given that it’s the same interface as for native events in the DOM, do we also have access to preventDefault
and stopPropagation
?
Absolutely! You can use these methods with the parameter pulled from the function passed to the event. In our case, you could have used e.preventDefault()
. If you need a refresher on preventDefault
and stopPropagation
, read this chapter on listening for events in the course, Write JavaScript for the Web.
Create Forms More Easily With React
React simplifies form management: you can easily access the value, whether it's an input checkbox, textarea, or even a select with onChange
.
There are two main ways to manage forms: controlled and uncontrolled. You should deal with the uncontrolled method swiftly because it requires less involvement from React, which encourages the use of controlled forms.
Delegate Control Using Uncontrolled Forms
Let's look at a demonstration of an uncontrolled form. We're going to put a QuestionForm
component directly in our app's App.js
. Then we'll declare it in a separate file and add a field for a question.
Here's a form that wraps our input:
<form onSubmit={handleSubmit}>
<input type='text' name='my_input' defaultValue='Type your text' />
<button type='submit'>Enter</button>
</form>
And for handleSubmit
, we get:
function handleSubmit(e) {
e.preventDefault()
alert(e.target['my_input'].value)
}
You’ll see that React allows us to specify a defaultValue
in the input field. Let's call preventDefault
; otherwise, submit
will refresh the page.
Our alert is triggered. 🥳
Pretty simple, isn’t it? You’re delegating the work to your DOM. Indeed, uncontrolled forms mean you don't have to manage too much information. However, this approach is a bit less React, because it has its limitations.
Instead, consider using controlled components.
Take Control of Your Forms
Local state allows you to hold specific component information and comes from an interaction that the user has had with a component.
Let's create an inputValue
variable and the function that we'll use to change its value in the local state with useState
.
The line of code below allows us to set the initial state for inputValue
and the corresponding function to modify it and to specify the default value "Ask your question here:"
const [inputValue, setInputValue] = useState("Ask your question here")
Which returns a QuestionForm
:
import { useState } from 'react'
function QuestionForm() {
const [inputValue, setInputValue] = useState('Ask your question here')
return (
<div>
<textarea
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
</div>
)
}
export default QuestionForm
We passed a callback function to onChange
to save our input value in our local state. We'll access the typed input value with e.target.value
.
inputValue
now has access to the content of our input at any time. We can create a button that triggers an alert to render our input content, like this:
<div>
<textarea
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={() => alert(inputValue)}>Alert me 🚨</button>
</div>
It works!
It’s great that I can access my input value, but why would I need to? 🤔
Understand the Benefits of Controlled Forms
It means you can interact directly with the user's information and display an error message if the data is invalid or filter the data to intercept invalid values.
If we decide that the letter ‘f’ is not valid (an odd example, I know), we can declare the variable:
const isInputError = inputValue.includes('f')
And display an error message (or not) according to this boolean:
{isInputError && (
<div>🔥 You are not allowed to use the letter “f” here.</div>
)}
Similarly, we can intercept invalid data entered by the user. To do this, declare an intermediate function:
function checkValue(value) {
if (!value.includes('f')) {
setInputValue(value)
}
}
And apply the modification in the callback function:
onChange={(e) => checkValue(e.target.value)}
Now, however many times you hammer away on the "f" key, the value will not register in your input.
You might not realize it just now, but this gives you great freedom as a developer in terms of the checks you want to create. You’ve got the power! 🔥
Great, I now know two methods, but when should I use controlled components and when should I use the uncontrolled version?!
It’s a case-by-case judgment. You’ll have to decide based on your constraints. When you have a quick component to do that isn't complex, uncontrolled input might be all you need. However, if you have checks to do, it’s certainly better to use a controlled component. There seem to be many more controlled components in codebases.
There are also libraries that you can use to manage forms and their validation as cleanly as possible, such as the tool react-hook-form.
And there you go: you’re now ready to interact with your users through events and forms. 😎
Give It a Go!
It’s now time to put all of that into practice. You’ll find the codebase you need to get started on branch P2C5-Begin.
Here's a to-do list of things you can do to improve the app:
Create an alert that is triggered when a user clicks on the CareScale component that says:
"This plant requires a small/moderate/large amount of light/water" depending on the corresponding data and whether it’s a water or light type CareScale component.
You’ll find a new
Footer
component in the codebase. To this, you should add:An input to get the user’s email address, applying the controlled component method. The state syntax used in this chapter for
inputValue
andsetInputValue
that uses useState is also written in the codebase.A
blur
event (when the user clicks outside of the field) that triggers an alert if inputValue doesn’t contain the @ character. This alert should say, "Error: no @ has been entered. This is not a valid email address."
You’ll find the solution on branch P2C5-Solution.
Let’s Recap!
In React, write events within a tag in
camelCase
, and pass the function to be called.Unlike in JS, you’ll seldom use
addEventListener
.React passes a synthetic event as a parameter of the callBack functions. This synthetic event is similar to a native event passed in the DOM, except that it is compatible with all browsers.
There are two main ways of managing form: controlled and uncontrolled. You are encouraged to use controlled forms.
Controlled forms save the field values in the local state and access the data entered by the user with
onChange
.Controlled forms allow you to filter content or display an error message based on the data entered by the user.
We've reached the end of Part 2. It’s now time to check your understanding in the quiz. Once you’ve done that, we can launch into learning about state with React!