DEV Community

loading...

CodeToday: Learning By Doing with React Hooks

Kurt Bauer
Both a Web & Mobile Developer with start-up experience, from front-end UI to back-end RESTful API design, my ultimate goal is to secure data privacy.
・5 min read

The Gist

I haven't had a chance to implement React's state hooks in a project yet, so I quickly found a tutorial on Scotch.io to dive into.
I wanted to document my journey through the project and questions I hit as I start using hooks.

The Journey

1) Question: First question I had as I built the below code, was on the syntax. Specifically, using useState([])

function App() {
  const [todos, setTodos] = useState([
    { text: "Learn about React" },
    { text: "Meet friend for lunch" },
    { text: "Build really cool todo app" }
  ]);
}

Answer: Just some regular destructuring, which "makes it possible to unpack values from arrays, or properties from objects, into distinct variables."

I was used to object destructuring:

const person = {first: 'Wes'}
const first = person.first; // 'Wes'

But with array destructuring, we don't have to worry about keys and values jumbling up our code. I quickly found a very clear post by Sarah Chima, called Destructuring Assignment in ES6- Arrays.
Two key basic things are that this array destructuring helps in grabbing our elements based on the array's index. And that commas help us skip over elements and grab the next element.

  var sentence = ["Kurt", "likes", "programming"];
  var [firstWord,, lastWord] = sentence;
  console.log(lastWord) // programming

So now, when I map over my todo array, the first element would look something like:

  console.log(todos[0]); // {text: "Learn about React"}

Now, my app is displaying a list of todos

todolist

2) Question: How to add items to my list?

const App = () => {
  //useState: 2 variables, name them anything.
  // firstVar = value || this.state
  // secondVar = function to update value || this.setState
  const [todos, setTodos] = useState([
    { text: "Learn about React" },
    { text: "Meet friend for lunch" },
    { text: "Build really cool todo app" }
  ]);

  const addTodo = text => {
    // spread operaotor = to create a copy of array
    // {text} = from TodoForm/form/input type=text
    const newTodos = [...todos, { text }]; //needs to be object
    setTodos(newTodos);
  };

  console.log(todos); // {text: "Learn about React"}

  return (
    <div className="app">
      <div className="todo-list">
        {todos.map((todo, index) => (
          <Todo key={index} index={index} todo={todo} />
        ))}
        <TodoForm addTodo={addTodo} />
      </div>
    </div>
  );
};

Answer: Where does the magic happen? Well first I had to create a TodoForm component. Still dealing with functional components and hooks, I just added the value for my variable, which will be blank at first. An onChange function was added into the input field, which then passes the value to the addTodo function that we're getting from App's state hook declaration. And finally we reset the value to be black with our setValue function call that works in the same way as setState does within Class components.

import React, { useState } from "react";

const TodoForm = ({ addTodo }) => {
  const [value, setValue] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue("");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        className="input"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
    </form>
  );
};

export default TodoForm;

Then inside of our app, after importing our TodoFormcomponent, we pass it the addToDo function as a prop. Let's take a look at that function. Here I'm pulling in the user's text, which is a property on our input field. I create a variable, and pass in a copy of my todos array with the spread operator. The second variable in my array is the text we will adding onto our array, and thus our list of tasks.

  const addTodo = text => {
    // spread operaotor = to create a copy of array
    // {text} = from TodoForm/form/input type=text
    const newTodos = [...todos, { text }]; //needs to be object
    setTodos(newTodos);
  };

3) Question: How do I update my list once I've completed a task?

Well most of the work for updating a task as complete is being done in the following callback function, inside my App.js file:

  const completedToDo = (index) => {
    // array holds copt of task list array
    const newTodos = [...todos];
    // find item by its index in array
    // access the isCompleted property

    newTodos[index].isCompleted === false
      ? (newTodos[index].isCompleted = true)
      : (newTodos[index].isCompleted = false);

    // set ToDo list to alrered array that was copied
    setTodos(newTodos);
  };

Answer: It bothered me that I could only mark a task as complete once and not undo it according to the tutorial so I added a ternary to toggle my isCompleted = false property that I added to all my task objects in their initial state. How this works is a Todo component is created with the completedToDo function. This function has access to the index attribute. My ToDo component now looks like:

const Todo = ({ todo, index, completedToDo }) => {
  // Set variabls to hold me strings here
  const complete = "Complete";
  const undo = "Undo";

  return (
    <div
      className="todo"
      style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
    >
      {todo.text}
      <div>
        <button onClick={() => completedToDo(index)}>
          {" "}
          {todo.isCompleted ? undo : complete}
        </button>
      </div>
    </div>
  );
};
export default Todo;

You can see that I have an onClick even handler that registers when I click a task button, and sends the index up to my completedToDo function. Depending on whether todo.isCompleted if flase or true I display different text. Not a huge change, but it makes it feel more like a task list. It's in my completedToDo function where I'm changing my boolean value. And then I'm using my react hook variable, setTodos, to update my react hook state.

newTodos[index].isCompleted === false
      ? (newTodos[index].isCompleted = true)
      : (newTodos[index].isCompleted = false);
setTodos(newTodos);

So that's mainly it! Now we have buttons that can be marked as completed, or if we accidentally hit it or realize there was something missing, we can always undo.

crossed out text

4) Question: How can I delete an item from my list?

Well it's basically a lot like the function I created to mark a task as completed.

  const removeTodo = index => {
    // create a copy of original array
    const newTodos = [...todos];
    // use splice() to remove item from array based on it's index
    // alters that copy of the array that we've made
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

Answer: We add this callback at a prop in our ToDo component, it grabs the index, I create a copy of my todos array, use the splice() method to remove an element from our array based on it's index. Then the new array copy with remove elements is set with setTodos.

And that's pretty much it! Now both you and I understand the basic of using the usState React Hook to add state to your functional components.

Final List

Conclusion

Scotch.io has some great tutorials, sometimes they can be outdated, but for the most part it's a great resource to have. Again, I didn't create this project, but wanted to talk through the parts I need to take a second to research. As you saw, hooks aren't that scary once you jump in! And a big thanks to Sarah Chima, follow her for some more cool walk-troughs and tutorials!

Oh and if you want to take a look at the coded version, I added a link to my CodeSandbox I created. I also advise using that or CodePen, since with the free version you can create an infinite amount of public projects that can be fairly compartmentalized with different files/ NPM packages.

Edit react-hooks-useState-intro

Discussion (0)