diff --git a/files/en-us/learn/tools_and_testing/client-side_javascript_frameworks/react_interactivity_events_state/index.md b/files/en-us/learn/tools_and_testing/client-side_javascript_frameworks/react_interactivity_events_state/index.md index 21d8620a17a411f..8ef228f8b16593b 100644 --- a/files/en-us/learn/tools_and_testing/client-side_javascript_frameworks/react_interactivity_events_state/index.md +++ b/files/en-us/learn/tools_and_testing/client-side_javascript_frameworks/react_interactivity_events_state/index.md @@ -37,9 +37,15 @@ With our component plan worked out, it's now time to start updating our app from ## Handling events -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: +If you've only written vanilla JavaScript before now, you might be used to having a separate JavaScript file in which you query for some DOM nodes and attach listeners to them. For example, an HTML file might have a button in it, like this: -```jsx +```html + +``` + +And a JavaScript file might have some code like this: + +```js const btn = document.querySelector("button"); btn.addEventListener("click", () => { @@ -47,7 +53,7 @@ btn.addEventListener("click", () => { }); ``` -In React, we write event handlers directly on the elements in our JSX, like this: +In JSX, the code that describes the UI lives right alongside our event listeners: ```jsx ``` -> **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 ` + + + + ); +} + +export default Todo; +``` + ## Deleting tasks from state and UI 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()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter). We can test each task, and exclude a task from the new array if its `id` prop matches the `id` argument passed into `deleteTask()`. -Update the `deleteTask()` function inside your `App.js` file as follows: +Update the `deleteTask()` function inside your `App.jsx` file as follows: ```jsx function deleteTask(id) { @@ -510,6 +589,77 @@ function deleteTask(id) { Try your app out again. Now you should be able to delete a task from your app! +At this point, your `App.jsx` file should look like this: + +```jsx +import { useState } from "react"; +import { nanoid } from "nanoid"; +import Todo from "./components/Todo"; +import Form from "./components/Form"; +import FilterButton from "./components/FilterButton"; + +function App(props) { + function addTask(name) { + const newTask = { id: `todo-${nanoid}`, name, completed: false }; + setTasks([...tasks, newTask]); + } + + function toggleTaskCompleted(id) { + const updatedTasks = tasks.map((task) => { + // if this task has the same ID as the edited task + if (id === task.id) { + // use object spread to make a new object + // whose `completed` prop has been inverted + return { ...task, completed: !task.completed }; + } + return task; + }); + setTasks(updatedTasks); + } + + function deleteTask(id) { + const remainingTasks = tasks.filter((task) => id !== task.id); + setTasks(remainingTasks); + } + + const [tasks, setTasks] = useState(props.tasks); + const taskList = tasks?.map((task) => ( + + )); + + const tasksNoun = taskList.length !== 1 ? "tasks" : "task"; + const headingText = `${taskList.length} ${tasksNoun} remaining`; + + return ( +
+

TodoMatic

+ +
+ + + +
+

{headingText}

+ +
+ ); +} + +export default App; +``` + ## Summary That's enough for one article. Here we've given you the lowdown on how React deals with events and handles state, and implemented functionality to add tasks, delete tasks, and toggle tasks as completed. We are nearly there. In the next article we'll implement functionality to edit existing tasks and filter the list of tasks between all, completed, and incomplete tasks. We'll look at conditional UI rendering along the way.