Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
With our component plan worked out, it's now time to start updating our app from a completely static UI to one that actually allows us to interact and change things. In this article we'll do this, digging into events and state along the way, and ending up with an app in which we can successfully add and delete tasks, and toggle tasks as completed.
If you've only written vanilla JavaScript before now, you might be used to having a separate JavaScript file, where you query for some DOM nodes and attach listeners to them. For example:
In React, we write event handlers directly on the elements in our JSX, like this:
Note: This may seem counter-intuitive regarding best-practice advice that tends to advise against use of inline event handlers on HTML, but remember that JSX is actually part of your JavaScript.
In the above example, we're adding an onClick
attribute to the <button>
element. The value of that attribute is a function that triggers a simple alert.
The onClick
attribute has special meaning here: it tells React to run a given function when the user clicks on the button. There are a couple of other things to note:
The camel-cased nature of onClick
is important — JSX will not recognize onclick
(again, it is already used in JavaScript for a specific purpose, which is related but different — standard onclick
handler properties).
All browser events follow this format in JSX – on
, followed by the name of the event.
Let's apply this to our app, starting in the Form.js
component.
At the top of the Form()
component function, create a function named handleSubmit()
. This function should prevent the default behavior of the submit
event. After that, it should trigger an alert()
, which can say whatever you'd like. It should end up looking something like this:
To use this function, add an onSubmit
attribute to the <form>
element, and set its value to the handleSubmit
function:
Now if you head back to your browser and click on the "Add" button, your browser will show you an alert dialog with the words "Hello, world!" — or whatever you chose to write there.
In React applications, interactivity is rarely confined to just one component: events that happen in one component will affect other parts of the app. When we start giving ourselves the power to make new tasks, things that happen in the <Form />
component will affect the list rendered in <App />
.
We want our handleSubmit()
function to ultimately help us create a new task, so we need a way to pass information from <Form />
to <App />
. We can't pass data from child to parent in the same way as we pass data from parent to child using standard props. Instead, we can write a function in <App />
that will expect some data from our form as an input, then pass that function to <Form />
as a prop. This function-as-a-prop is called a callback prop. Once we have our callback prop, we can call it inside <Form />
to send the right data to <App />
.
Inside the top of our App()
component function, create a function named addTask()
which has a single parameter of name
:
Next, we'll pass addTask()
into <Form />
as a prop. The prop can have whatever name you want, but pick a name you'll understand later. Something like addTask
works, because it matches the name of the function as well as what the function will do. Your <Form />
component call should be updated as follows:
Finally, you can use this prop inside the handleSubmit()
function in your <Form />
component! Update it as follows:
Clicking on the "Add" button in your browser will prove that the addTask()
callback function works, but it'd be nice if we could get the alert to show us what we're typing in our input field! This is what we'll do next.
Note: We decided to name our callback prop addTask
to make it easy to understand what the prop will do. Another common convention you may well come across in React code is to prefix callback prop names with the word on
, followed by the name of the event that will cause them to be run. For instance, we could have given our form a prop of onSubmit
with the value of addTask
.
So far, we've used props to pass data through our components and this has served us just fine. Now that we're dealing with user input and data updates, however, we need something more.
For one thing, props come from the parent of a component. Our <Form />
will not be inheriting a new name for our task; our <input />
element lives directly inside of <Form />
, so <Form/>
will be directly responsible for creating that new name. We can't ask <Form />
to spontaneously create its own props, but we can ask it to track some of its own data for us. Data such as this, which a component itself owns, is called state. State is another powerful tool for React because components not only own state, but can update it later. It's not possible to update the props a component receives; only to read them.
React provides a variety of special functions that allow us to provide new capabilities to components, like state. These functions are called hooks, and the useState
hook, as its name implies, is precisely the one we need in order to give our component some state.
To use a React hook, we need to import it from the react module. In Form.js
, change your very first line so that it reads like this:
This allows us to import the useState()
function by itself, and utilize it anywhere in this file.
useState()
creates a piece of state for a component, and its only parameter determines the initial value of that state. It returns two things: the state, and a function that can be used to update the state later.
This is a lot to take in at once, so let's try it out. We're going to make ourselves a name
state, and a function for updating the name
state.
Write the following above your handleSubmit()
function, inside Form()
:
What's going on in this line of code?
We are setting the initial name
value as "Use hooks!".
We are defining a function whose job is to modify name
, called setName()
.
useState()
returns these two things, so we are using array destructuring to capture them both in separate variables.
You can see the name
state in action right away. Add a value
attribute to the form's input, and set its value to name
. Your browser will render "Use hooks!" inside the input.
Change "Use hooks!" to an empty string once you're done; this is what we want for our initial state.
Before we can change the value of name
, we need to capture a user's input as they type. For this, we can listen to the onChange
event. Let's write a handleChange()
function, and listen for it on the <input />
tag.
Currently, your input's value will not change as you type, but your browser will log the word "Typing!" to the JavaScript console, so we know our event listener is attached to the input. In order to change the input's value, we have to use our handleChange()
function to update our name
state.
To read the contents of the input field as they change, you can access the input's value
property. We can do this inside handleChange()
by reading e.target.value
. e.target
represents the element that fired the change
event — that's our input. So, value
is the text inside it.
You can console.log()
this value to see it in your browser's console.
Logging isn't enough — we want to actually store the updated state of the name as the input value changes! Change the console.log()
to setName()
, as shown below:
Now we need to change our handleSubmit()
function so that it calls props.addTask
with name as an argument — remember our callback prop? This will serve to send the task back to the App
component, so we can add it to our list of tasks at some later date. As a matter of good practice, you should clear the input after your form submits, so we'll call setName()
again with an empty string to do so:
At last, you can type something into the input field in your browser and click Add — whatever you typed will appear in an alert dialog.
Your Form.js
file should now read like this:
Note: One thing you'll notice is that you are able to submit empty tasks by just pressing the Add button without entering a task name. Can you think of a way to disallow empty tasks from being added? As a hint, you probably need to add some kind of check into the handleSubmit()
function.
Now that we've practiced with events, callback props, and hooks we're ready to write functionality that will allow a user to add a new task from their browser.
Import useState
into App.js
, so that we can store our tasks in state — update your React
import line to the following:
We want to pass props.tasks
into the useState()
hook – this will preserve its initial state. Add the following right at the top of your App() function definition:
Now, we can change our taskList
mapping so that it is the result of mapping tasks
, instead of props.tasks
. Your taskList
constant declaration should now look like so:
We've now got a setTasks
hook that we can use in our addTask()
function to update our list of tasks. There's one problem however: we can't just pass the name
argument of addTask()
into setTasks
, because tasks
is an array of objects and name
is a string. If we tried to do this, the array would be replaced with the string.
First of all, we need to put name
into an object that has the same structure as our existing tasks. Inside of the addTask()
function, we will make a newTask
object to add to the array.
We then need to make a new array with this new task added to it and then update the state of the tasks data to this new state. To do this, we can use spread syntax to copy the existing array, and add our object at the end. We then pass this array into setTasks()
to update the state.
Putting that all together, your addTask()
function should read like so:
Now you can use the browser to add a task to our data! Type anything into the form and click "Add" (or press the Enter key) and you'll see your new todo item appear in the UI!
However, we have another problem: our addTask()
function is giving each task the same id
. This is bad for accessibility, and makes it impossible for React to tell future tasks apart with the key
prop. In fact, React will give you a warning in your DevTools console — "Warning: Encountered two children with the same key..."
We need to fix this. Making unique identifiers is a hard problem – one for which the JavaScript community has written some helpful libraries. We'll use nanoid because it's tiny, and it works.
Make sure you're in the root directory of your application and run the following terminal command:
Note: If you're using yarn, you'll need the following instead: yarn add nanoid
Now we can import nanoid
into the top of App.js
so we can use it to create unique IDs for our new tasks. First of all, include the following import line at the top of App.js
:
Now let's update addTask()
so that each task ID becomes a prefix todo- plus a unique string generated by nanoid. Update your newTask
constant declaration to this:
Save everything, and try your app again — now you can add tasks without getting that warning about duplicate IDs.
Now that we can add new tasks, you may notice a problem: our heading reads 3 tasks remaining, no matter how many tasks we have! We can fix this by counting the length of taskList
and changing the text of our heading accordingly.
Add this inside your App()
definition, before the return statement:
Hrm. This is almost right, except that if our list ever contains a single task, the heading will still use the word "tasks". We can make this a variable, too. Update the code you just added as follows:
Now you can replace the list heading's text content with the headingText
variable. Update your <h2>
like so:
You might notice that, when you click on a checkbox, it checks and unchecks appropriately. As a feature of HTML, the browser knows how to remember which checkbox inputs are checked or unchecked without our help. This feature hides a problem, however: toggling a checkbox doesn't change the state in our React application. This means that the browser and our app are now out-of-sync. We have to write our own code to put the browser back in sync with our app.
Before we fix the problem, let's observe it happening.
We'll start by writing a toggleTaskCompleted()
function in our App()
component. This function will have an id
parameter, but we're not going to use it yet. For now, we'll log the first task in the array to the console – we're going to inspect what happens when we check or uncheck it in our browser:
Add this just above your taskList
constant declaration:
Next, we'll add toggleTaskCompleted
to the props of each <Todo />
component rendered inside our taskList
; update it like so:
Next, go over to your Todo.js
component and add an onChange
handler to your <input />
element, which should use an anonymous function to call props.toggleTaskCompleted()
with a parameter of props.id
. The <input />
should now look like this:
Save everything and return to your browser and notice that our first task, Eat, is checked. Open your JavaScript console, then click on the checkbox next to Eat. It unchecks, as we expect. Your JavaScript console, however, will log something like this:
The checkbox unchecks in the browser, but our console tells us that Eat is still completed. We will fix that next!
Let's revisit our toggleTaskCompleted()
function in App.js
. We want it to change the completed
property of only the task that was toggled, and leave all the others alone. To do this, we'll map()
over the task list and just change the one we completed.
Update your toggleTaskCompleted()
function to the following:
Here, we define an updatedTasks
constant that maps over the original tasks
array. If the task's id
property matches the id
provided to the function, we use object spread syntax to create a new object, and toggle the checked
property of that object before returning it. If it doesn't match, we return the original object.
Then we call setTasks()
with this new array in order to update our state.
Deleting a task will follow a similar pattern to toggling its completed state: We need to define a function for updating our state, then pass that function into <Todo />
as a prop and call it when the right event happens.
Here we'll start by writing a deleteTask()
function in your App
component. Like toggleTaskCompleted()
, this function will take an id
parameter, and we will log that id
to the console to start with. Add the following below toggleTaskCompleted()
:
Next, add another callback prop to our array of <Todo />
components:
In Todo.js
, we want to call props.deleteTask()
when the "Delete" button is pressed. deleteTask()
needs to know the ID of the task that called it, so it can delete the correct task from the state.
Update the "Delete" button inside Todo.js, like so:
Now when you click on any of the "Delete" buttons in the app, your browser console should log the ID of the related task.
Now that we know deleteTask()
is invoked correctly, we can call our setTasks()
hook in deleteTask()
to actually delete that task from the app's state as well as visually in the app UI. Since setTasks()
expects an array as an argument, we should provide it with a new array that copies the existing tasks, excluding the task whose ID matches the one passed into deleteTask()
.
This is a perfect opportunity to use Array.prototype.filter()
. We can test each task, and exclude a task from the new array if its id
prop matches the id
parameter passed into deleteTask()
.
Update the deleteTask()
function inside your App.js
file as follows:
Try your app out again. Now you should be able to delete a task from your app!
Editing, filtering, conditional rendering
As we near the end of our React journey (for now at least), we'll add the finishing touches to the main areas of functionality in our Todo list app. This includes allowing you to edit existing tasks, and filtering the list of tasks between all, completed, and incomplete tasks. We'll look at conditional UI rendering along the way.
We don't have a user interface for editing the name of a task yet. We'll get to that in a moment. To start with, we can at least implement an editTask()
function in App.js
. It'll be similar to deleteTask()
because it'll take an id
to find its target object, but it'll also take a newName
property containing the name to update the task to. We'll use Array.prototype.map()
instead of Array.prototype.filter()
because we want to return a new array with some changes, instead of deleting something from the array.
Add the editTask()
function inside your App component, in the same place as the other functions:
Pass editTask
into our <Todo />
components as a prop in the same way we did with deleteTask
:
Now open Todo.js
. We're going to do some refactoring.
In order to allow users to edit a task, we have to provide a user interface for them to do so. First, import useState
into the Todo
component like we did before with the App
component, by updating the first import statement to this:
We'll now use this to set an isEditing
state, the default state of which should be false
. Add the following line just inside the top of your Todo(props) { … }
component definition:
Next, we're going to rethink the <Todo />
component — from now on, we want it to display one of two possible "templates", rather than the single template it's used so far:
The "view" template, when we are just viewing a todo; this is what we've used in the tutorial thus far.
The "editing" template, when we are editing a todo. We're about to create this.
Copy this block of code into the Todo()
function, beneath your useState()
hook but above the return
statement:
We've now got the two different template structures — "edit" and "view" — defined inside two separate constants. This means that the return
statement of <Todo />
is now repetitious — it also contains a definition of the "view" template. We can clean this up by using conditional rendering to determine which template the component returns, and is therefore rendered in the UI.
In JSX, we can use a condition to change what is rendered by the browser. To write a condition in JSX, we can use a ternary operator.
In the case of our <Todo />
component, our condition is "Is this task being edited?" Change the return
statement inside Todo()
so that it reads like so:
Your browser should render all your tasks just like before. To see the editing template, you will have to change the default isEditing
state from false
to true
in your code for now; we will look at making the edit button toggle this in the next section!
At long last, we are ready to make our final core feature interactive. To start with, we want to call setEditing()
with a value of true
when a user presses the "Edit" button in our viewTemplate
, so that we can switch templates.
Update the "Edit" button in the viewTemplate
like so:
Now we'll add the same onClick
handler to the "Cancel" button in the editingTemplate
, but this time we'll set isEditing
to false
so that it switches us back to the view template.
Update the "Cancel" button in the editingTemplate
like so:
With this code in place, you should be able to press the "Edit" and "Cancel" buttons in your todo items to toggle between templates.
The next step is to actually make the editing functionality work.
Much of what we're about to do will mirror the work we did in Form.js
: as the user types in our new input field, we need to track the text they enter; once they submit the form, we need to use a callback prop to update our state with the new name of the task.
We'll start by making a new hook for storing and setting the new name. Still in Todo.js
, put the following underneath the existing hook:
Next, create a handleChange()
function that will set the new name; put this underneath the hooks but before the templates:
Now we'll update our editingTemplate
's <input />
field, setting a value
attribute of newName
, and binding our handleChange()
function to its onChange
event. Update it as follows:
Finally, we need to create a function to handle the edit form's onSubmit
event; add the following just below the previous function you added:
Remember that our editTask()
callback prop needs the ID of the task we're editing as well as its new name.
Bind this function to the form's submit
event by adding the following onSubmit
handler to the editingTemplate
's <form>
:
You should now be able to edit a task in your browser!
Now that our main features are complete, we can think about our filter buttons. Currently, they repeat the "All" label, and they have no functionality! We will be reapplying some skills we used in our <Todo />
component to:
Create a hook for storing the active filter.
Render an array of <FilterButton />
elements that allow users to change the active filter between all, completed, and incomplete.
Add a new hook to your App()
function that reads and sets a filter. We want the default filter to be All
because all of our tasks should be shown initially:
Our goal right now is two-fold:
Each filter should have a unique name.
Each filter should have a unique behavior.
A JavaScript object would be a great way to relate names to behaviors: each key is the name of a filter; each property is the behavior associated with that name.
At the top of App.js
, beneath our imports but above our App()
function, let's add an object called FILTER_MAP
:
The values of FILTER_MAP
are functions that we will use to filter the tasks
data array:
The All
filter shows all tasks, so we return true
for all tasks.
The Active
filter shows tasks whose completed
prop is false
.
The Completed
filter shows tasks whose completed
prop is true
.
Beneath our previous addition, add the following — here we are using the Object.keys()
method to collect an array of FILTER_NAMES
:
Note: We are defining these constants outside our App()
function because if they were defined inside it, they would be recalculated every time the <App />
component re-renders, and we don't want that. This information will never change no matter what our application does.
Now that we have the FILTER_NAMES
array, we can use it to render all three of our filters. Inside the App()
function we can create a constant called filterList
, which we will use to map over our array of names and return a <FilterButton />
component. Remember, we need keys here, too.
Add the following underneath your taskList
constant declaration:
Now we'll replace the three repeated <FilterButton />
s in App.js
with this filterList
. Replace the following:
With this:
This won't work yet. We've got a bit more work to do first.
To make our filter buttons interactive, we should consider what props they need to utilize.
We know that the <FilterButton />
should report whether it is currently pressed, and it should be pressed if its name matches the current value of our filter state.
We know that the <FilterButton />
needs a callback to set the active filter. We can make direct use of our setFilter
hook.
Update your filterList
constant as follows:
In the same way as we did earlier with our <Todo />
component, we now have to update FilterButton.js
to utilize the props we have given it. Do each of the following, and remember to use curly braces to read these variables!
Replace all
with {props.name}
.
Set the value of aria-pressed
to {props.isPressed}
.
Add an onClick
handler that calls props.setFilter()
with the filter's name.
With all of that done, your FilterButton()
function should read like this:
Visit your browser again. You should see that the different buttons have been given their respective names. When you press a filter button, you should see its text take on a new outline — this tells you it has been selected. And if you look at your DevTool's Page Inspector while clicking the buttons, you'll see the aria-pressed
attribute values change accordingly.
However, our buttons still don't actually filter the todos in the UI! Let's finish this off.
Right now, our taskList
constant in App()
maps over the tasks state and returns a new <Todo />
component for all of them. This is not what we want! A task should only render if it is included in the results of applying the selected filter. Before we map over the tasks state, we should filter it (with Array.prototype.filter()
) to eliminate objects we don't want to render.
Update your taskList
like so:
In order to decide which callback function to use in Array.prototype.filter()
, we access the value in FILTER_MAP
that corresponds to the key of our filter state. When filter is All
, for example, FILTER_MAP[filter]
will evaluate to () => true
.
Choosing a filter in your browser will now remove the tasks that do not meet its criteria. The count in the heading above the list will also change to reflect the list!
So that's it — our app is now functionally complete. However, now that we've implemented all of our features, we can make a few improvements to ensure that a wider range of users can use our app. Our next article rounds things off for our React tutorials by looking at including focus management in React, which can improve usability and reduce confusion for both keyboard-only and screenreader users.
At this point, we've accomplished all of the features we set out to implement. A user can add a new task, check and uncheck tasks, delete tasks, or edit task names. And they can filter their task list by all, active, or completed tasks.
Or, at least: they can do all of these things with a mouse. Unfortunately, these features are not very accessible to keyboard-only users. Let's explore this now.
Start by clicking on the input at the top of our app, as if you're going to add a new task. You'll see a thick, dashed outline around that input. This outline is your visual indicator that the browser is currently focused on this element. Press the Tab key, and you will see the outline appear around the "Add" button beneath the input. This shows you that the browser's focus has moved.
Press Tab a few more times, and you will see this dashed focus indicator move between each of the filter buttons. Keep going until the focus indicator is around the first "Edit" button. Press Enter.
The <Todo />
component will switch templates, as we designed, and you'll see a form that lets us edit the name of the task.
But where did our focus indicator go?
When we switch between templates in our <Todo />
component, we completely remove the elements that were there before to replace them with something else. That means the element that we were focused on vanishes, and nothing is in focus at all. This could confuse a wide variety of users — particularly users who rely on the keyboard, or users who use a screen reader.
To improve the experience for keyboard and screen-reader users, we should manage the browser's focus ourselves.
When a user toggles a <Todo/>
template from viewing to editing, we should focus on the <input>
used to rename it; when they toggle back from editing to viewing, we should move focus back to the "Edit" button.
Change the import
statement at the top of Todo.js
so that it includes useRef
:
Then, create two new constants beneath the hooks in your Todo()
function. Each should be a ref – one for the "Edit" button in the view template and one for the edit field in the editing template.
These refs have a default value of null
because they will not have value until we attach them to their respective elements. To do that, we'll add an attribute of ref
to each element, and set their values to the appropriately named ref
objects.
The textbox <input>
in your editing template should be updated like this:
The "Edit" button in your view template should read like this:
Change the import statement of Todo.js
again to add useEffect
:
useEffect()
takes a function as an argument; this function is executed after the component renders. Let's see this in action; put the following useEffect()
call just above the return
statement in the body of Todo()
, and pass into it a function that logs the words "side effect" to your console:
To illustrate the difference between the main render process and code run inside useEffect()
, add another log – put this one below the previous addition:
Now, open the app in your browser. You should see both messages in your console, with each one repeating three times. Note how "main render" logged first, and "side effect" logged second, even though the "side effect" log appears first in the code.
That's it for our experimentation for now. Delete console.log("main render")
now, and lets move on to implementing our focus management.
Now that we know our useEffect()
hook works, we can manage focus with it. As a reminder, we want to focus on the editing field when we switch to the editing template.
Update your existing useEffect()
hook so that it reads like this:
These changes make it so that, if isEditing
is true, React reads the current value of the editFieldRef
and moves browser focus to it. We also pass an array into useEffect()
as a second argument. This array is a list of values useEffect()
should depend on. With these values included, useEffect()
will only run when one of those values changes. We only want to change focus when the value of isEditing
changes.
Try it now, and you'll see that when you click an "Edit" button, focus moves to the corresponding edit <input>
!
At first glance, getting React to move focus back to our "Edit" button when the edit is saved or cancelled appears deceptively easy. Surely we could add a condition to our useEffect
to focus on the edit button if isEditing
is false
? Let's try it now — update your useEffect()
call like so:
This kind of mostly works. Head back to your browser and you'll see that your focus moves between Edit <input>
and "Edit" button as you start and end an edit. However, you may have noticed a new problem — the "Edit" button in the final <Todo />
component is focussed immediately on page load, before we even interact with the app!
Our useEffect()
hook is behaving exactly as we designed it: it runs as soon as the component renders, sees that isEditing
is false
, and focuses the "Edit" button. Because there are three instances of <Todo />
, we see focus on the last "Edit" button.
We need to refactor our approach so that focus changes only when isEditing
changes from one value to another.
In order to meet our refined criteria, we need to know not just the value of isEditing
, but also when that value has changed. In order to do that, we need to be able to read the previous value of the isEditing
constant. Using pseudocode, our logic should be something like this:
Paste the following code near the top of Todo.js
, above your Todo()
function.
Now we'll define a wasEditing
constant beneath the hooks at the top of Todo()
. We want this constant to track the previous value of isEditing
, so we call usePrevious
with isEditing
as an argument:
With this constant, we can update our useEffect()
hook to implement the pseudocode we discussed before — update it as follows:
Note that the logic of useEffect()
now depends on wasEditing
, so we provide it in the array of dependencies.
Again try using the "Edit" and "Cancel" buttons to toggle between the templates of your <Todo />
component; you'll see the browser focus indicator move appropriately, without the problem we discussed at the start of this section.
There's one last keyboard experience gap: when a user deletes a task from the list, the focus vanishes. We're going to follow a pattern similar to our previous changes: we'll make a new ref, and utilize our usePrevious()
hook, so that we can focus on the list heading whenever a user deletes a task.
Sometimes, the place we want to send our focus to is obvious: when we toggled our <Todo />
templates, we had an origin point to "go back" to — the "Edit" button. In this case however, since we're completely removing elements from the DOM, we have no place to go back to. The next best thing is an intuitive location somewhere nearby. The list heading is our best choice because it's close to the list item the user will delete, and focusing on it will tell the user how many tasks are left.
Import the useRef()
and useEffect()
hooks into App.js
— you'll need them both below:
Then declare a new ref inside the App()
function. Just above the return
statement is a good place:
Let's add the tabindex
attribute — written as tabIndex
in JSX — to the heading above our list of tasks, along with our headingRef
:
Note: The tabindex
attribute is great for accessibility edge-cases, but you should take great care to not overuse it. Only apply a tabindex
to an element when you're absolutely sure that making it focusable will benefit your user in some way. In most cases, you should be utilizing elements that can naturally take focus, such as buttons, anchors, and inputs. Irresponsible usage of tabindex
could have a profoundly negative impact on keyboard and screen-reader users!
We want to focus on the element associated with our ref (via the ref
attribute) only when our user deletes a task from their list. That's going to require the usePrevious()
hook we already used earlier on. Add it to the top of your App.js
file, just below the imports:
Now add the following, above the return
statement inside the App()
function:
Here we are invoking usePrevious()
to track the length of the tasks state, like so:
Note: Since we're now utilizing usePrevious()
in two files, a good efficiency refactor would be to move the usePrevious()
function into its own file, export it from that file, and import it where you need it. Try doing this as an exercise once you've got to the end.
Now that we've stored how many tasks we previously had, we can set up a useEffect()
hook to run when our number of tasks changes, which will focus the heading if the number of tasks we have now is less than with it previously was — i.e. we deleted a task!
Add the following into the body of your App()
function, just below your previous additions:
We only try to focus on our list heading if we have fewer tasks now than we did before. The dependencies passed into this hook ensure it will only try to re-run when either of those values (the number of current tasks, or the number of previous tasks) changes.
Now, when you delete a task in your browser, you will see our dashed focus outline appear around the heading above the list.
In order to focus on an element in our DOM, we need to tell React which element we want to focus on and how to find it. React's hook creates an object with a single property: current
. This property can be a reference to anything we want and look that reference up later. It's particularly useful for referring to DOM elements.
To use our refs for their intended purpose, we need to import another React hook: . useEffect()
is so named because it runs after React renders a given component, and will run any side-effects that we'd like to add to the render process, which we can't run inside the main function body. useEffect()
is useful in the current situation because we cannot focus on an element until after the <Todo />
component renders and React knows where our refs are.
The React team had discussed , and has provided an example custom hook we can use for the job.
Heading elements like our <h2>
are not usually focusable. This isn't a problem — we can make any element programmatically focusable by adding the attribute to it. This means only focusable with JavaScript. You can't press Tab to focus on an element with a tabindex of -1
the same way you could do with a or element (this can be done using tabindex="0"
, but that's not really appropriate in this case).
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. You can find a detailed component API reference here.
Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called "props") and return React elements describing what should appear on the screen.
The simplest way to define a component is to write a JavaScript function:
This function is a valid React component because it accepts a single "props" (which stands for properties) object argument with data and returns a React element. We call such components "function components" because they are literally JavaScript functions.
You can also use an ES6 class to define a component:
The above two components are equivalent from React's point of view.
Function and Class components both have some additional features that we will discuss in the next sections.
Previously, we only encountered React elements that represent DOM tags:
However, elements can also represent user-defined components:
When React sees an element representing a user-defined component, it passes JSX attributes and children to this component as a single object. We call this object "props".
For example, this code renders "Hello, Sara" on the page:
Let's recap what happens in this example:
We call ReactDOM.render()
with the <Welcome name="Sara" />
element.
React calls the Welcome
component with {name: 'Sara'}
as the props.
Our Welcome
component returns a <h1>Hello, Sara</h1>
element as the result.
React DOM efficiently updates the DOM to match <h1>Hello, Sara</h1>
.
Note: Always start component names with a capital letter.
React treats components starting with lowercase letters as DOM tags. For example,
<div />
represents an HTML div tag, but<Welcome />
represents a component and requiresWelcome
to be in scope.To learn more about the reasoning behind this convention, please read JSX In Depth.
Components can refer to other components in their output. This lets us use the same component abstraction for any level of detail. A button, a form, a dialog, a screen: in React apps, all those are commonly expressed as components.
For example, we can create an App
component that renders Welcome
many times:
Typically, new React apps have a single App
component at the very top. However, if you integrate React into an existing app, you might start bottom-up with a small component like Button
and gradually work your way to the top of the view hierarchy.
Don't be afraid to split components into smaller components.
For example, consider this Comment
component:
It accepts author
(an object), text
(a string), and date
(a date) as props, and describes a comment on a social media website.
This component can be tricky to change because of all the nesting, and it is also hard to reuse individual parts of it. Let's extract a few components from it.
First, we will extract Avatar
:
The Avatar
doesn't need to know that it is being rendered inside a Comment
. This is why we have given its prop a more generic name: user
rather than author
.
We recommend naming props from the component's own point of view rather than the context in which it is being used.
We can now simplify Comment
a tiny bit:
Next, we will extract a UserInfo
component that renders an Avatar
next to the user's name:
This lets us simplify Comment
even further:
Extracting components might seem like grunt work at first, but having a palette of reusable components pays off in larger apps. A good rule of thumb is that if a part of your UI is used several times (Button
, Panel
, Avatar
), or is complex enough on its own (App
, FeedStory
, Comment
), it is a good candidate to be extracted to a separate component.
Whether you declare a component as a function or a class, it must never modify its own props. Consider this sum
function:
Such functions are called "pure" because they do not attempt to change their inputs, and always return the same result for the same inputs.
In contrast, this function is impure because it changes its own input:
React is pretty flexible but it has a single strict rule:
All React components must act like pure functions with respect to their props.
Of course, application UIs are dynamic and change over time. In the next section, we will introduce a new concept of "state". State allows React components to change their output over time in response to user actions, network responses, and anything else, without violating this rule.
Importing
ReactTestUtils
makes it easy to test React components in the testing framework of your choice. At Facebook we use Jest for painless JavaScript testing. Learn how to get started with Jest through the Jest website's React Tutorial.
Note:
We recommend using React Testing Library which is designed to enable and encourage writing tests that use your components as the end users do.
For React versions <= 16, the Enzyme library makes it easy to assert, manipulate, and traverse your React Components' output.
act()
To prepare a component for assertions, wrap the code rendering it and performing updates inside an act()
call. This makes your test run closer to how React works in the browser.
Note
If you use
react-test-renderer
, it also provides anact
export that behaves the same way.
For example, let's say we have this Counter
component:
Here is how we can test it:
Don't forget that dispatching DOM events only works when the DOM container is added to the document
. You can use a library like React Testing Library to reduce the boilerplate code.
The recipes
document contains more details on how act()
behaves, with examples and usage.
mockComponent()
Pass a mocked component module to this method to augment it with useful methods that allow it to be used as a dummy React component. Instead of rendering as usual, the component will become a simple <div>
(or other tag if mockTagName
is provided) containing any provided children.
Note:
mockComponent()
is a legacy API. We recommend usingjest.mock()
instead.
isElement()
Returns true
if element
is any React element.
isElementOfType()
Returns true
if element
is a React element whose type is of a React componentClass
.
isDOMComponent()
Returns true
if instance
is a DOM component (such as a <div>
or <span>
).
isCompositeComponent()
Returns true
if instance
is a user-defined component, such as a class or a function.
isCompositeComponentWithType()
Returns true
if instance
is a component whose type is of a React componentClass
.
findAllInRenderedTree()
Traverse all components in tree
and accumulate all components where test(component)
is true
. This is not that useful on its own, but it's used as a primitive for other test utils.
scryRenderedDOMComponentsWithClass()
Finds all DOM elements of components in the rendered tree that are DOM components with the class name matching className
.
findRenderedDOMComponentWithClass()
Like scryRenderedDOMComponentsWithClass()
but expects there to be one result, and returns that one result, or throws exception if there is any other number of matches besides one.
scryRenderedDOMComponentsWithTag()
Finds all DOM elements of components in the rendered tree that are DOM components with the tag name matching tagName
.
findRenderedDOMComponentWithTag()
Like scryRenderedDOMComponentsWithTag()
but expects there to be one result, and returns that one result, or throws exception if there is any other number of matches besides one.
scryRenderedComponentsWithType()
Finds all instances of components with type equal to componentClass
.
findRenderedComponentWithType()
Same as scryRenderedComponentsWithType()
but expects there to be one result and returns that one result, or throws exception if there is any other number of matches besides one.
renderIntoDocument()
Render a React element into a detached DOM node in the document. This function requires a DOM. It is effectively equivalent to:
Note:
You will need to have
window
,window.document
andwindow.document.createElement
globally available before you importReact
. Otherwise React will think it can't access the DOM and methods likesetState
won't work.
Simulate
Simulate an event dispatch on a DOM node with optional eventData
event data.
Simulate
has a method for every event that React understands.
Clicking an element
Changing the value of an input field and then pressing ENTER.
Note
You will have to provide any event property that you're using in your component (e.g. keyCode, which, etc...) as React is not creating any of these for you.
This page is an overview of the React documentation and related resources.
React is a JavaScript library for building user interfaces. Learn what React is all about on our homepage or in the tutorial.
React has been designed from the start for gradual adoption, and you can use as little or as much React as you need. Whether you want to get a taste of React, add some interactivity to a simple HTML page, or start a complex React-powered app, the links in this section will help you get started.
You can add React to an HTML page in one minute. You can then either gradually expand its presence, or keep it contained to a few dynamic widgets.
When starting a React project, a simple HTML page with script tags might still be the best option. It only takes a minute to set up!
As your application grows, you might want to consider a more integrated setup. There are several JavaScript toolchains we recommend for larger applications. Each of them can work with little to no configuration and lets you take full advantage of the rich React ecosystem. Learn how.
People come to React from different backgrounds and with different learning styles. Whether you prefer a more theoretical or a practical approach, we hope you'll find this section helpful.
If you prefer to learn by doing, start with our practical tutorial.
If you prefer to learn concepts step by step, start with our guide to main concepts.
Like any unfamiliar technology, React does have a learning curve. With practice and some patience, you will get the hang of it.
The React homepage contains a few small React examples with a live editor. Even if you don't know anything about React yet, try changing their code and see how it affects the result.
The React documentation assumes some familiarity with programming in the JavaScript language. You don't have to be an expert, but it's harder to learn both React and JavaScript at the same time.
Tip
If you prefer to learn by doing, check out our practical tutorial. In this tutorial, we build a tic-tac-toe game in React. You might be tempted to skip it because you're not into building games -- but give it a chance. The techniques you'll learn in the tutorial are fundamental to building any React apps, and mastering it will give you a much deeper understanding.
If you prefer to learn concepts step by step, our guide to main concepts is the best place to start. Every next chapter in it builds on the knowledge introduced in the previous chapters so you won't miss anything as you go along.
Many React users credit reading Thinking in React as the moment React finally "clicked" for them. It's probably the oldest React walkthrough but it's still just as relevant.
Sometimes people find third-party books and video courses more helpful than the official documentation. We maintain a list of commonly recommended resources, some of which are free.
Once you're comfortable with the main concepts and played with React a little bit, you might be interested in more advanced topics. This section will introduce you to the powerful, but less commonly used React features like context and refs.
This documentation section is useful when you want to learn more details about a particular React API. For example, React.Component
API reference can provide you with details on how setState()
works, and what different lifecycle methods are useful for.
The glossary contains an overview of the most common terms you'll see in the React documentation. There is also a FAQ section dedicated to short questions and answers about common topics, including making AJAX requests, component state, and file structure.
The React blog is the official source for the updates from the React team. Anything important, including release notes or deprecation notices, will be posted there first.
This documentation always reflects the latest stable version of React. Since React 16, you can find older versions of the documentation on a separate page. Note that documentation for past versions is snapshotted at the time of the release, and isn't being continuously updated.
A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.
A common pattern is for a component to return a list of children. Take this example React snippet:
<Columns />
would need to return multiple <td>
elements in order for the rendered HTML to be valid. If a parent div was used inside the render()
of <Columns />
, then the resulting HTML will be invalid.
results in a <Table />
output of:
Fragments solve this problem.
which results in a correct <Table />
output of:
There is a new, shorter syntax you can use for declaring fragments. It looks like empty tags:
You can use <></>
the same way you'd use any other element except that it doesn't support keys or attributes.
Fragments declared with the explicit <React.Fragment>
syntax may have keys. A use case for this is mapping a collection to an array of fragments -- for example, to create a description list:
key
is the only attribute that can be passed to Fragment
. In the future, we may add support for additional attributes, such as event handlers.
If you're interested in playing around with React, you can use an online code playground. Try a Hello World template on , , or .
If you prefer to use your own text editor, you can also , edit it, and open it from the local filesystem in your browser. It does a slow runtime code transformation, so we'd only recommend using this for simple demos.
If you feel that the React documentation goes at a faster pace than you're comfortable with, check out . It introduces the most important React concepts in a detailed, beginner-friendly way. Once you're done, give the documentation another try!
If you're coming from a design background, are a great place to get started.
We recommend going through to check your knowledge level. It will take you between 30 minutes and an hour but you will feel more confident learning React.
Whenever you get confused by something in JavaScript, and are great websites to check. There are also community support forums where you can ask for help.
You can also follow the on Twitter, but you won't miss anything essential if you only read the blog.
Not every React release deserves its own blog post, but you can find a detailed changelog for every release in the , as well as on the page.
If something is missing in the documentation or if you found some part confusing, please with your suggestions for improvement, or tweet at the . We love hearing from you!
There is also a new for declaring them.
You can try out the new JSX fragment syntax with this .
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
The Effect Hook lets you perform side effects in function components:
This snippet is based on the counter example from the previous page, but we added a new feature to it: we set the document title to a custom message including the number of clicks.
Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you're used to calling these operations "side effects" (or just "effects"), you've likely performed them in your components before.
Tip
If you're familiar with React class lifecycle methods, you can think of
useEffect
Hook ascomponentDidMount
,componentDidUpdate
, andcomponentWillUnmount
combined.
There are two common kinds of side effects in React components: those that don't require cleanup, and those that do. Let's look at this distinction in more detail.
Sometimes, we want to run some additional code after React has updated the DOM. Network requests, manual DOM mutations, and logging are common examples of effects that don't require a cleanup. We say that because we can run them and immediately forget about them. Let's compare how classes and Hooks let us express such side effects.
In React class components, the render
method itself shouldn't cause side effects. It would be too early -- we typically want to perform our effects after React has updated the DOM.
This is why in React classes, we put side effects into componentDidMount
and componentDidUpdate
. Coming back to our example, here is a React counter class component that updates the document title right after React makes changes to the DOM:
Note how we have to duplicate the code between these two lifecycle methods in class.
This is because in many cases we want to perform the same side effect regardless of whether the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render -- but React class components don't have a method like this. We could extract a separate method but we would still have to call it in two places.
Now let's see how we can do the same with the useEffect
Hook.
We've already seen this example at the top of this page, but let's take a closer look at it:
What does useEffect
do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we'll refer to it as our "effect"), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.
Why is useEffect
called inside a component? Placing useEffect
inside the component lets us access the count
state variable (or any props) right from the effect. We don't need a special API to read it -- it's already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution.
Does useEffect
run after every render? Yes! By default, it runs both after the first render and after every update. (We will later talk about how to customize this.) Instead of thinking in terms of "mounting" and "updating", you might find it easier to think that effects happen "after render". React guarantees the DOM has been updated by the time it runs the effects.
Now that we know more about effects, these lines should make sense:
We declare the count
state variable, and then we tell React we need to use an effect. We pass a function to the useEffect
Hook. This function we pass is our effect. Inside our effect, we set the document title using the document.title
browser API. We can read the latest count
inside the effect because it's in the scope of our function. When React renders our component, it will remember the effect we used, and then run our effect after updating the DOM. This happens for every render, including the first one.
Experienced JavaScript developers might notice that the function passed to useEffect
is going to be different on every render. This is intentional. In fact, this is what lets us read the count
value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a different effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result -- each effect "belongs" to a particular render. We will see more clearly why this is useful later on this page.
Tip
Unlike
componentDidMount
orcomponentDidUpdate
, effects scheduled withuseEffect
don't block the browser from updating the screen. This makes your app feel more responsive. The majority of effects don't need to happen synchronously. In the uncommon cases where they do (such as measuring the layout), there is a separateuseLayoutEffect
Hook with an API identical touseEffect
.
Earlier, we looked at how to express side effects that don't require any cleanup. However, some effects do. For example, we might want to set up a subscription to some external data source. In that case, it is important to clean up so that we don't introduce a memory leak! Let's compare how we can do it with classes and with Hooks.
In a React class, you would typically set up a subscription in componentDidMount
, and clean it up in componentWillUnmount
. For example, let's say we have a ChatAPI
module that lets us subscribe to a friend's online status. Here's how we might subscribe and display that status using a class:
Notice how componentDidMount
and componentWillUnmount
need to mirror each other. Lifecycle methods force us to split this logic even though conceptually code in both of them is related to the same effect.
Note
Eagle-eyed readers may notice that this example also needs a
componentDidUpdate
method to be fully correct. We'll ignore this for now but will come back to it in a later section of this page.
Let's see how we could write this component with Hooks.
You might be thinking that we'd need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that useEffect
is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up:
Why did we return a function from our effect? This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They're part of the same effect!
When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time. We'll discuss why this helps avoid bugs and how to opt out of this behavior in case it creates performance issues later below.
Note
We don't have to return a named function from the effect. We called it
cleanup
here to clarify its purpose, but you could return an arrow function or call it something different.
We've learned that useEffect
lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function:
Other effects might not have a cleanup phase, and don't return anything.
The Effect Hook unifies both use cases with a single API.
If you feel like you have a decent grasp on how the Effect Hook works, or if you feel overwhelmed, you can jump to the next page about Rules of Hooks now.
We'll continue this page with an in-depth look at some aspects of useEffect
that experienced React users will likely be curious about. Don't feel obligated to dig into them now. You can always come back to this page to learn more details about the Effect Hook.
One of the problems we outlined in the Motivation for Hooks is that class lifecycle methods often contain unrelated logic, but related logic gets broken up into several methods. Here is a component that combines the counter and the friend status indicator logic from the previous examples: