curious_Coder
curious_Coder

Reputation: 27

How to display different tables on click in React?

I am making a simple todo. I am fetching data from an API and I want to show all the items in a table by default. There will be 3 buttons - All, Complete and Incomplete which will show All, Completed and Incompleted todos table respectively. I have set states for completed and incompleted todos but can't wrap my head around how to perform conditional rendering and display different tables on different button clicks.

Below is my code -

import React, { useState, useEffect } from "react";
import axios from "axios";
import "./style.css";

export default function App() {

  const URL = 'https://jsonplaceholder.typicode.com/todos';

  const [todo, setTodo] = useState([]);
  const [completed, setCompleted] = useState([]);
  const [incomplete, setIncomplete] = useState([]);

  useEffect(()=>{
    axios.get(URL)
    .then(res=>setTodo(res.data));
  },[])

  const showCompleted = () =>{
    const completeTask = todo.filter((items)=>items.completed===true);
    setCompleted(completeTask);
  }

  const showIncomplete = () =>{
    const incompleteTask = todo.filter((items)=>items.completed===false);
    setIncomplete(incompleteTask);
  }

  return (
    <div>
      <h1>ToDos!</h1>
      <button type="button">All</button>
      <button type="button" onClick={showCompleted}>Completed</button>
      <button type="button" onClick={showIncomplete}>Incomplete</button>
      <hr />
      <table>
        <tr>
          <th>ID</th>
          <th>Title</th>
          <th>Completed</th>
        </tr>
        {todo.map((items)=>
          <tr key={items.id}>
          <td>{items.id}</td>
          <td>{items.title}</td>
          <td><input type="checkbox" defaultChecked={items.completed ? true : false} /></td>
          </tr>
          )}
      </table>
    </div>
  );
} 

Upvotes: 1

Views: 1126

Answers (4)

Sergey Sosunov
Sergey Sosunov

Reputation: 4600

You can use useMemo to prepare the data to display based on some conditions/filters/search/ordering/ anything else.

So few steps to achieve that:

  1. Optional, declare some object outside of the component to hold some constants. Maybe I choosed a poor name for that but the idea itself should be ok. FILTER_COMPLETED in the code.

  2. Add a useState variable to hold active filter for this specific area. const [filterCompleteMode, setFilterCompleteMode] = useState(...) in the code.

  3. Add a useMemo variable that will prepare the data to display. You can apply some ordering or additinal filtering here. todosToDisplay in the code.

  4. Modify your JSX a bit, change <button>s and todo to todosToDisplay.

const { useState, useMemo, useEffect } = React;

const FILTER_COMPLETED = {
  All: "ALL",
  Complete: "COMPLETE",
  Incomplete: "INCOMPLETE"
};

function App() {
  const URL = "https://jsonplaceholder.typicode.com/todos";

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

  const [filterCompleteMode, setFilterCompleteMode] = useState(
    FILTER_COMPLETED.All
  );

  const todosToDisplay = useMemo(() => {
    if (!todos) return [];
    switch (filterCompleteMode) {
      case FILTER_COMPLETED.All:
        return todos;
      case FILTER_COMPLETED.Incomplete:
        return todos.filter((x) => x.completed === false);
      case FILTER_COMPLETED.Complete:
        return todos.filter((x) => x.completed === true);
      default:
        return todos;
    }
  }, [todos, filterCompleteMode]);

  useEffect(() => {
    fetch(URL)
      .then((res) => res.json())
      .then((data) => setTodos(data));
  }, []);

  const onCompleteFilterClick = (e) => {
    setFilterCompleteMode(e.target.dataset.mode);
  };

  return (
    <div>
      <h1>ToDos!</h1>
      <button
        type="button"
        data-mode={FILTER_COMPLETED.All}
        onClick={onCompleteFilterClick}
      >
        All
      </button>
      <button
        type="button"
        data-mode={FILTER_COMPLETED.Complete}
        onClick={onCompleteFilterClick}
      >
        Completed
      </button>
      <button
        type="button"
        data-mode={FILTER_COMPLETED.Incomplete}
        onClick={onCompleteFilterClick}
      >
        Incomplete
      </button>
      <hr />
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Completed</th>
          </tr>
        </thead>
        <tbody>
          {todosToDisplay.map((item) => (
            <tr key={item.id}>
              <td>{item.id}</td>
              <td>{item.title}</td>
              <td>
                <input
                  type="checkbox"
                  defaultChecked={item.completed ? true : false}
                />
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>

Upvotes: 1

Andy
Andy

Reputation: 63587

  1. Instead of maintaining a separate state for each type have one type state that the buttons update when they're clicked. Add data attributes to the buttons to indicate what type they are and which can be picked up in the click handler.

  2. Instead of mapping over the whole set of todos, call a function that filters out the set of data from the todo state that you need.

const { useEffect, useState } = React;

const URL = 'https://jsonplaceholder.typicode.com/todos';

function Example() {

  const [todos, setTodos] = useState([]);
  const [type, setType] = useState('all');

  useEffect(()=>{
    fetch(URL)
      .then(res => res.json())
      .then(data => setTodos(data));
  }, []);

  // Filter the todos depending on type
  function filterTodos(type) {
    switch(type) {
      case 'completed': {
        return todos.filter(todo => todo.completed);
      }
      case 'incomplete': {
        return todos.filter(todo => !todo.completed);
      }
      default: return todos;
    }
  }

  // Set the type when the buttons are clicked
  function handleClick(e) {
    const { type } = e.target.dataset;
    setType(type);
  }

  // Call the filter function to get the
  // subset of todos that you need based
  // on the type
  return (
    <div>
      <h1>ToDos!</h1>
      <button
        type="button"
        className={type === 'all' && 'active'}
        data-type="all"
        onClick={handleClick}
      >All
      </button>
      <button
        type="button"
        className={type === 'completed' && 'active'}
        data-type="completed"
        onClick={handleClick}
      >Completed
      </button>
      <button
        type="button"
        className={type === 'incomplete' && 'active'}
        data-type="incomplete"
        onClick={handleClick}
      >Incomplete
      </button>
      <hr />
      <table>
        <tr>
          <th>ID</th>
          <th>Title</th>
          <th>Completed</th>
        </tr>
        {filterTodos(type).map(todo => {
          const { id, title, completed } = todo;
          return (
            <tr key={id}>
              <td>{id}</td>
              <td>{title}</td>
              <td>
                <input
                  type="checkbox"
                  defaultChecked={completed ? true : false}
                />
              </td>
            </tr>
          );
        })}
      </table>
    </div>
  );

}

ReactDOM.render(
  <Example />,
  document.getElementById('react')
);
button { margin-right: 0.25em; }
button:hover { cursor:pointer; }
.active { background-color: lightgreen; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Upvotes: 2

Amila Senadheera
Amila Senadheera

Reputation: 13265

Keep two states, one to store the initial data and another one to keep track of actually displayed data.

Try like this:

function App() {
  const URL = "https://jsonplaceholder.typicode.com/todos";

  const [todo, setTodo] = React.useState([]);
  const [view, setView] = React.useState([]);

  React.useEffect(() => {
    fetch(URL)
      .then((res) => res.json())
      .then((result) => {
        setTodo(result);
        setView(result);
      });
  }, []);

  const showAll = () => {
    setView(todo);
  };

  const showCompleted = () => {
    const completeTask = todo.filter((items) => items.completed === true);
    setView(completeTask);
  };

  const showIncomplete = () => {
    const incompleteTask = todo.filter((items) => items.completed === false);
    setView(incompleteTask);
  };

  return (
    <div>
      <h1>ToDos!</h1>
      <button type="button" onClick={showAll}>
        All
      </button>
      <button type="button" onClick={showCompleted}>
        Completed
      </button>
      <button type="button" onClick={showIncomplete}>
        Incomplete
      </button>
      <hr />
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Completed</th>
          </tr>
        </thead>
        <tbody>
          {view.map((items) => (
            <tr key={items.id}>
              <td>{items.id}</td>
              <td>{items.title}</td>
              <td>
                <input
                  type="checkbox"
                  defaultChecked={items.completed ? true : false}
                />
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

Upvotes: 1

Gibloor
Gibloor

Reputation: 141

Create state:

 const [whatShow, setWhatShow] = useState('All').

When you click on button change this state next:

  {todo.map((items)=>
    {items.completed === whatShow && <tr key={items.id}>
      <td>{items.id}</td>
      <td>{items.title}</td>
      <td><input type="checkbox" defaultChecked={items.completed ? true : false} /></td>
    </tr>}

)}

something like this

Upvotes: 0

Related Questions