DEV Community

Cover image for Built a Filterable To-do App in React
Yasmin Ledesma
Yasmin Ledesma

Posted on

Built a Filterable To-do App in React

Introduction

Yeah, one could say this is the ol' reliable in terms of personal projects, so it's actually funny that this is my first time making one. Well, I had made one before, when I was more of a newbie than I am now, but it was pretty basic and, to be honest, I left the project without understanding much.

Now, this one Frontend Mentor challenge lived up to its title, and taught me so, so many new things about React. You should definitely give it a try if you're looking for your next project!

Please keep in mind that this is by no means a guide to build a todo list in React. This whole article is simply my way of documenting my experience doing it, the issues I found along the way, and the solutions I came up with to solve them. This is going to be a short one.

Table of Contents

Overview of the challenge

The original challenge can be found at Frontend Mentor.

If this one is not the most propular challenge there, I have no idea which one is: people have made almost 10 thousand attemps at it! But it only has a completion rate of 10%...

Basically, it challenges you to make a simple to-do app that can perform basic to-do list functions like adding, completing, and removing tasks. It should also remember the user's tasks, and allow them to change the theme from light to dark.

The tricky part about it is allowing the user to filter their list based on the state of compleition of their added tasks. And, oh boy, being able to figure out how to implement that, trying it out, failling, then going back to the thinking phase, was how about 90% of my time was spend working with this project.

The process

Challenge N° The One and Only... at least this time around.

I usually start a project like this by writing the structure, then styling it. Making every separate component, and then adding them to the layout as a sort of mock-up app, for the lack of a good term. Finally, I get down to business to write the code that makes the app run like it's suppossed to.

And this time, the process was so surpringly, relatively smooth? But there was a catch, obviously, as there always is. When I reached the filtering buttons, I said to myself:

"Okay, just apply filter to my todos array with some conditions, then hook those buttons up."

const [todos, setTodos] = useState([]);

...

const filterCompleted = () => {
  return todos.filter( todo => {
    if(todo.completed === true) {
      return todo;
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

I did so right away, and right away I found myself with an irreversibly mutated state that basically trashed all my stored tasks little by little, every time I pressed one of those buttons. And so, the thinking grind started.

My next solution failed. Then my next one worked, but it had problems. I'm not going to bore you with all the detailts of my try-and-fail's. It's enough to say I eventually arrived to a pretty good solution.

After learning to store data inside the broswer with localStorage's methods so the user's tasks would render even after they left and came back, I realized I could apply that same logic with my todos state.

const [renderize, setRenderize] = useState(todos);
Enter fullscreen mode Exit fullscreen mode

First, I set up another state (renderize) with the useState hook, because I found out I cound't use useReducer more than once inside the same component. Then, I made up a function to use as a callback in my filter buttons.

const handleFilter = (e) => {
    if (e.target.innerHTML === "All") {
      setRenderize(todos);
    }

    if (e.target.innerHTML === "Active") {
      setRenderize(
        [...todos].filter((todo) => {
          if (todo.completed === false) {
            return todo;
          }
        })
      );
    }

    if (e.target.innerHTML === "Completed") {
      setRenderize(
        [...todos].filter((todo) => {
          if (todo.completed === true) {
            return todo;
          }
        })
      );
    }
  };
Enter fullscreen mode Exit fullscreen mode

It probably lacks some refactoring, I know. But it filters the todos array and returns whatever set of tasks the user might want to see, and stores them in renderize, which updates everytime one of those three buttons is clicked, or everytime a new task is added to the list. Finally, renderize is what is mapped inside an unordered list, with any necessary props passed down to the <Task /> component.

I'm still not sure how to apply useReducer in this app to manage multiple states in the same component. I guess I could just make a object containing all of them, and that's what I'll try to do next time, just for the sake of tidying everything up. But them, I'm sure doing something like that would cause undesired side-effects, so I'll may have to come up with something else.

Features left to add and issues left to solve

Even though this project was the most challenging for me as of now, I got out of it leaving nothing to cross out of the list.

There's this issue with the layout transitioning in weird ways when the user changes the size of their viewport, but that's not really an action users usually perform, so I'm leaving that for future me to fix.

The most pressing non-issue in my mind right now is to make the browser remember what theme color the user prefers. I mean, who likes to get shot-blasted with a pearly white screen at, frankly, any time of the day? I mean, I don't mind myself, I'm #TeamLightTheme4Ever, but most people aren't. So, I'll be researching about how to manipulate and store the state that manages it, then give you an update here.

And it's done!

You can find my solution to this challenge in its repository. You can also try it out live!

Screenshot

Where to find me...

You can find me on GitHub and Twitter, where I occasionally share my experience as a beginner developer.

Latest comments (0)