Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Struggling with passing the state object array which is initially is empty

I try to pass the array of objects to table component to display it. The error I get is pretty logical, however I can’t find a way to fix this issue. I marked a line with the problem I keep getting:

Type 'Expense[] | null | undefined' is not assignable to type 'Expense[] | undefined'.
  Type 'null' is not assignable to type 'Expense[] | undefined'.ts(2322)

Is there a way to avoid passing a null value or ignoring it if it is passed?

App.tsx:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import { useState } from "react";
import ExpenseList from "./ExpenseTacker/Components/ExpenseList/ExpenseList";
import ExpenseFilter from "./ExpenseTacker/Components/ExpenseFilter/ExpenseFilter";
import ExpenseForm from "./ExpenseTacker/Components/ExpenseForm/ExpenseForm";
import categories from "./ExpenseTacker/categories";

interface Expense {
  id: number;
  description: string;
  amount: number;
  category: string;
}

function App() {
  const [selectedCategory, setSelectedCategory] = useState("");
  const [expenses, setExpanses] = useState<Expense[] | null>(null);

  const visibleExpenses = selectedCategory
    ? expenses?.filter((e: Expense) => e.category === selectedCategory)
    : expenses;
  if (expenses === null) {
    return <div>The table is empty</div>;
  } else {
    return (
      <div>
        <div className="mb-5">
          <ExpenseForm
            onSubmbit={(newExpense) =>
              setExpanses([
                ...(expenses || []),
                { ...newExpense, id: expenses.length + 1 },
              ])
            }
          />
        </div>
        <div className="mb-3">
          <ExpenseFilter
            onSelectCategory={(category) => setSelectedCategory(category)}
          ></ExpenseFilter>
        </div>

        <ExpenseList
          expenses={visibleExpenses} //THE PROBLEM LINE
          onDelete={(id) => setExpanses(expenses.filter((e) => e.id != id))}
        />
      </div>
    );
  }
}

export default App;

ExpenseList.tsx (table file):

    import React, { useState } from "react";
import { TbFileDescription } from "react-icons/tb";
import { RiMoneyDollarBoxLine } from "react-icons/ri";
import { MdOutlineCategory } from "react-icons/md";

interface Expense {
  id: number;
  description: string;
  amount: number;
  category: string;
}

interface Props {
  expenses?: Expense[];
  onDelete: (id: number) => void;
}

const ExpenseList = ({ expenses, onDelete }: Props) => {
  if (expenses?.length === 0) return null;
  return (
    <table className="table table-bordered">
      <thead>
        <tr>
          <th scope="col">
            <TbFileDescription/> Description
          </th>
          <th scope="col">
            <RiMoneyDollarBoxLine/> Amount
          </th>
          <th scope="col">
            <MdOutlineCategory/> Category
          </th>
          <th scope="col"></th>
        </tr>
      </thead>
      <tbody>
        {expenses?.map((expense) => (
          <tr key={expense.id}>
            <td>{expense.description}</td>
            <td>{expense.amount}</td>
            <td>{expense.category}</td>
            <td>
              <button
                className="btn btn-outline-danger"
                onClick={() => onDelete(expense.id)}
              >
                Delete
              </button>
            </td>
          </tr>
        ))}
      </tbody>
      <tfoot>
        <tr>
          <td>Total</td>
          <td>
            $
            {expenses?.reduce((acc, expense) => expense.amount + acc, 0)
              .toFixed(2)}
          </td>
          <td></td>
          <td></td>
        </tr>
      </tfoot>
    </table>
  );
};

export default ExpenseList;

>Solution :

you have error because visibleExpenses variable passed to ExpenseList component possible null value sometimes

you can fix with 3 solution:

  1. change condition :
import { useState } from "react";
import ExpenseList from "./ExpenseTacker/Components/ExpenseList/ExpenseList";
import ExpenseFilter from "./ExpenseTacker/Components/ExpenseFilter/ExpenseFilter";
import ExpenseForm from "./ExpenseTacker/Components/ExpenseForm/ExpenseForm";
import categories from "./ExpenseTacker/categories";

interface Expense {
  id: number;
  description: string;
  amount: number;
  category: string;
}

function App() {
  const [selectedCategory, setSelectedCategory] = useState("");
  const [expenses, setExpanses] = useState<Expense[] | null>(null);

  const visibleExpenses = selectedCategory
    ? expenses?.filter((e: Expense) => e.category === selectedCategory)
    : expenses;
  if (expenses === null || visibleExpenses === null) // => change this condition
  {
    return <div>The table is empty</div>;
  } else {
    return (
      <div>
        <div className="mb-5">
          <ExpenseForm
            onSubmbit={(newExpense) =>
              setExpanses([
                ...(expenses || []),
                { ...newExpense, id: expenses.length + 1 },
              ])
            }
          />
        </div>
        <div className="mb-3">
          <ExpenseFilter
            onSelectCategory={(category) => setSelectedCategory(category)}
          ></ExpenseFilter>
        </div>

        <ExpenseList
          expenses={visibleExpenses} //THE PROBLEM LINE
          onDelete={(id) => setExpanses(expenses.filter((e) => e.id != id))}
        />
      </div>
    );
  }
}

export default App;

  1. Change type of Props of ExpectationList component
import React, { useState } from "react";
import { TbFileDescription } from "react-icons/tb";
import { RiMoneyDollarBoxLine } from "react-icons/ri";
import { MdOutlineCategory } from "react-icons/md";

interface Expense {
  id: number;
  description: string;
  amount: number;
  category: string;
}

interface Props {
  expenses?: Expense[] | null;
  onDelete: (id: number) => void;
}

const ExpenseList = ({ expenses = [], onDelete }: Props) => {
  if (expenses?.length === 0) return null;
  return (
    <table className="table table-bordered">
      <thead>
        <tr>
          <th scope="col">
            <TbFileDescription/> Description
          </th>
          <th scope="col">
            <RiMoneyDollarBoxLine/> Amount
          </th>
          <th scope="col">
            <MdOutlineCategory/> Category
          </th>
          <th scope="col"></th>
        </tr>
      </thead>
      <tbody>
        {expenses?.map((expense) => (
          <tr key={expense.id}>
            <td>{expense.description}</td>
            <td>{expense.amount}</td>
            <td>{expense.category}</td>
            <td>
              <button
                className="btn btn-outline-danger"
                onClick={() => onDelete(expense.id)}
              >
                Delete
              </button>
            </td>
          </tr>
        ))}
      </tbody>
      <tfoot>
        <tr>
          <td>Total</td>
          <td>
            $
            {expenses?.reduce((acc, expense) => expense.amount + acc, 0)
              .toFixed(2)}
          </td>
          <td></td>
          <td></td>
        </tr>
      </tfoot>
    </table>
  );
};

export default ExpenseList;
  1. Apply the above two methods together
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading