Searching and Filtering Todos

Introduction to Searching and Filtering Todos

Welcome to the next step in enhancing your to-do application! In this lesson, we will focus on implementing search and filtering functionalities for your to-do items. These features are essential for managing and organizing tasks efficiently, especially as your list grows. By the end of this lesson, you'll be able to create a more user-friendly interface that allows users to quickly find and filter tasks based on their needs.

What You'll Learn

In this lesson, you will learn how to add search functionality to your to-do application. This involves allowing users to input a search term and filter the list of to-do items based on that term. Here's a quick look at how this is done in the Todos.js file:

import React, { useState, useEffect } from 'react';
import { List, CircularProgress, TextField } from '@mui/material';
import { getTodos } from './TodoService';
import TodoItem from './TodoItem';

function Todos() {
  const [todos, setTodos] = useState([]); // State to store the list of todos
  const [loading, setLoading] = useState(true); // State to manage loading status
  const [error, setError] = useState(null); // State to handle errors
  const [search, setSearch] = useState(''); // State to store the search term

  useEffect(() => {
    const fetchTodos = async () => {
      try {
        const data = await getTodos(); // Fetch todos from the service
        setTodos(data); // Set the fetched todos to state
      } catch (error) {
        setError(error); // Set error if fetching fails
      } finally {
        setLoading(false); // Set loading to false after fetching
      }
    };

    fetchTodos(); // Call the fetch function
  }, []);

  // Filter todos based on the search term
  const filteredTodos = todos.filter((todo) =>
    todo.title.toLowerCase().includes(search.toLowerCase())
  );

  if (loading) {
    return <CircularProgress />; // Show loading indicator
  }

  if (error) {
    return <div>Error: {error.message}</div>; // Show error message
  }

  return (
    <div>
      <h1>Todo List</h1>
      <TextField
        label="Search"
        value={search}
        onChange={(e) => setSearch(e.target.value)} // Update search term on input change
      />
      <List>
        {filteredTodos.map((todo) => (
          <TodoItem key={todo.id} todo={todo} /> // Render filtered todos
        ))}
      </List>
    </div>
  );
}

export default Todos;

In this code, we use a TextField to capture the user's search input and filter the list of to-do items based on the search term. This makes it easy for users to find specific tasks quickly.

Server-Side Searching and Filtering

While implementing search and filtering on the client side is effective for smaller datasets, there are scenarios where performing these operations on the server side is more beneficial. This is especially true when dealing with large datasets, as it reduces the amount of data transferred over the network and improves performance.

Why Server-Side Searching and Filtering?

  1. Performance: For large datasets, fetching all data to the client and then filtering can be inefficient. Server-side filtering allows you to send only the relevant data to the client, reducing load times and improving performance.

  2. Scalability: As your application grows, server-side operations can handle larger volumes of data more efficiently than client-side operations.

  3. Security: By filtering data on the server, you can ensure that only authorized data is sent to the client, enhancing security.

Constructing a Fetch Request for Server-Side Filtering

To implement server-side searching and filtering, you need to modify your fetch request to include query parameters that specify the search criteria. Here's an example of how you might construct such a request:

import React, { useState, useEffect } from 'react';
import { List, CircularProgress, TextField } from '@mui/material';
import TodoItem from './TodoItem';

function Todos() {
  const [todos, setTodos] = useState([]); // State to store the list of todos
  const [loading, setLoading] = useState(true); // State to manage loading status
  const [error, setError] = useState(null); // State to handle errors
  const [search, setSearch] = useState(''); // State to store the search term

  useEffect(() => {
    const fetchTodos = async () => {
      try {
        // Fetch todos with search query parameter
        const response = await fetch(`/api/todos?search=${encodeURIComponent(search)}`);
        if (!response.ok) {
          throw new Error('Network response was not ok'); // Handle network errors
        }
        const data = await response.json(); // Parse JSON response
        setTodos(data); // Set the fetched todos to state
      } catch (error) {
        setError(error); // Set error if fetching fails
      } finally {
        setLoading(false); // Set loading to false after fetching
      }
    };

    fetchTodos(); // Call the fetch function whenever search term changes
  }, [search]);

  if (loading) {
    return <CircularProgress />; // Show loading indicator
  }

  if (error) {
    return <div>Error: {error.message}</div>; // Show error message
  }

  return (
    <div>
      <h1>Todo List</h1>
      <TextField
        label="Search"
        value={search}
        onChange={(e) => setSearch(e.target.value)} // Update search term on input change
      />
      <List>
        {todos.map((todo) => (
          <TodoItem key={todo.id} todo={todo} /> // Render todos
        ))}
      </List>
    </div>
  );
}

export default Todos;

In this example, the fetch request includes a query parameter search that is sent to the server. The server is expected to handle this parameter and return a filtered list of to-do items based on the search term. This approach ensures that only the necessary data is sent to the client, optimizing performance and scalability.

Why It Matters

Implementing search and filtering capabilities is crucial for improving the usability of your application. As the number of tasks increases, it becomes challenging to locate specific items without these features. By enabling users to search and filter their to-do list, you enhance their experience and make your application more efficient and effective.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal