Skip to content

Lesson 1.4

Rebekah Callari-Kaczmarczyk edited this page Feb 8, 2024 · 2 revisions

This lesson will teach you the following:

  • Lifting State
  • Controlled Components
  • Props handling

Instructions

Overview

React has one-way data flow (i.e. unidirectional data flow) which means data will flow down from the parent component to the child components. There is a saying “React data flows down, and events flow up”.

Unidirectional data flow means that you send data from parent to child with props and you update data from child to parent with events. Other libraries such as Angular or Vue have two-way data flow (bidirectional data flow).

When you update the state, it may not immediately update your stateful variables. Instead, it actually just schedules, or enqueues an update to the component’s state. The reason for this behavior is it reduces the number of unnecessary component re-renders which improves performance.

The asynchronous nature of updating state can be a cause of bugs or unexpected behavior in a React app. The problem occurs that if you try to use the stateful variable immediately after you update it you may not get the most current state value.

If your state change is dependent on the current state, you can alleviate this issue by passing a function to your state setter function, rather than that a value or variable.

setCounter((prevCount) => {return prevCount+1});

When you pass a function to the setter function (setCounter), that function will receive the current state of the stateful variable (prevCount). The function that you pass to the setter function is called an updater function. The updater function is guaranteed to get the latest state value as a parameter.

If your stateful variable contains an object or array, you can replace the value by passing in a new object or array. But, if your new state depends on the old state, you’ll need to make a copy of the existing array or object, modify it, and then pass the copy of the array or object into the setter function.

Having a lot of components that each independently maintains their own state can increase the complexity of your app. To turn stateful components into stateless components, React developers use a technique called “lifting state up”.

This means that, instead of a component controlling its own state, you can have a component at a higher level in the hierarchy store the state. This state can then be passed down as props to the components that need it. This is very commonly done when sibling components need to share the same state as well. Lifting state up gives you the benefit of having fewer components that can cause your user interface to change, it makes your components more easily reusable, and makes your app easier to test.

Now put this into action...

Move Todo List into State

  • Open /src/App.jsx
  • Create new state variable named todoList with setter setTodoList and default value of an empty Array
  • Pass todoList state as a prop named todoList to the TodoList component
  • Open /src/TodoList.jsx
  • Add props as a parameter to the TodoList functional component
  • Change todoList to reference props instead of the hard-coded variable
  • Delete the hard-coded todoList variable
  • Run your application and view in browser
    • Verify that your Todo List is now empty (no list items)

Control "Add Todo" Input

  • Open /src/AddTodoForm.jsx
  • Create new state variable named todoTitle with setter setTodoTitle
  • Modify the <input> element to be a controlled input
    • Add value prop equal to todoTitle from component props
    • Add onChange prop equal to handleTitleChange function reference (we will declare this function in the next step)
  • Above the handleAddTodo function, declare a new function named handleTitleChange that takes event as a parameter
    • First, retrieve the input value from the event object and store in variable named newTodoTitle
    • Then, call the state setter setTodoTitle and pass newTodoTitle
  • In the handleAddTodo function, remove the todoTitle variable and update onAddTodo callback handler to pass our todoTitle state variable instead
  • Run your application and view in browser
    • Enter a new todo in "Add Todo" form, submit, and verify that the title appears below

Add New Todo to List

  • Open /src/App.jsx
  • Remove the newTodo state variable and the corresponding JSX that displays it
  • Declare a new function named addTodo that takes newTodo as a parameter
    • Call the setTodoList state setter and use the spread operator to pass the existing Objects in the todoList Array along with the newTodo Object
  • Change the value of the onAddTodo prop for AddTodoForm to addTodo
  • Open /src/AddTodoForm.jsx
  • Inside handleAddTodo, update the onAddTodo callback prop to pass an Object instead of a String; Object should have the following properties:
    • title: equal to todoTitle
    • id: unique identifier (hint: use Date.now() to generate a unique number)
      • Disclaimer: we are suggesting Date.now() for now as a placeholder for unique number generation, but in the future you should not use this
  • Inside handleAddTodo, remove the reset() method and replace it with logic to reset the todoTitle state to an empty String
  • Run your application and view in browser
    • Enter a todo in "Add Todo" form, submit, and verify item is visible in todo list
    • Enter another todo, submit, and verify that two items are visible in todo list

Destructure Props

  • Open /src/TodoList.jsx and update props to use destructuring
  • Open /src/TodoListItem.jsx and update props to use destructuring
  • Open /src/AddTodoForm.jsx and update props to use destructuring