Search code examples
reactjsoperator-keywordternary

Ternary operator shows for milliseconds the truthy part even when it's false



(sorry for my English)
I'm new in Reactjs and I'm trying to do an application where the user can create Flashcards.
The flashcards must have a category so, when the user enter in the menu to list and create flashcards, if there isn't any category created, I want to show a phrase for the user to access before the category menu to create a category.
To do that, my component that lists de flashcards receive an Prop with the list of categories and if the list is empty, it show the banner, if not, it show the rest of the component:

type Props = {
          categorias: Categoria[];
        };
        
const TarjetasList = ({ categorias }: Props) => { ...
    return (
        <>
          {!categorias.length ? (
            <h1>Debe crear una categoria</h1>
          ) : (
            <>
              <TarjetaFilter onSubmitFilter={handleSubmitFilter} />
              <div>
                {page?.content.map((tarjeta) => (
                  <div key={tarjeta.id}>
                    <TarjetaCard tarjeta={tarjeta} onDelete={getTarjetas} />
                  </div>
                ))}
              </div>
              <Pagination
                pageCount={page ? page?.totalPages : 0}
                range={2}
                onChange={handlePageChange}
              />
            </>
          )}
        </>
      );
    };

The problem is that when the user accesses the menu that lists the cards, it shows the banner with the phrase "You must create a category" for a few milliseconds, even though the "!categoria.length" is false, and then it stops display it and it displays the rest of the component as it should, but even though it's only for a few milliseconds, I want to avoid that. Is it possible? What am I doing wrong?

here is the code of the component of the menu that opens de list of cards:

const Tarjetas = () => {
  const [categorias, setCategorias] = useState<Categoria[]>([]);

  const getCategorias = useCallback(() => {
    const config: AxiosRequestConfig = {
      method: "GET",
      url: "/categorias",
      withCredentials: true,
    };

    requestBackend(config).then((response) => {
      setCategorias(response.data.content);
    });
  }, []);

  useEffect(() => {
    getCategorias();
  }, [getCategorias]);

  return (
    <Routes>
      <Route index element={<TarjetasList categorias={categorias} />} />
      <Route path=":tarjetaId" element={<TarjetasForm />} />
    </Routes>
  );
};

Here is the complete code of the TarjetasList component:

type ControlComponentsData = {
  activePage: number;
  filterData: TarjetaFilterData;
};

type Props = {
  categorias: Categoria[];
};

const TarjetasList = ({ categorias }: Props) => {
  const [page, setPage] = useState<SpringPage<Tarjeta>>();
  const [controlComponentsData, setControlComponentsData] =
    useState<ControlComponentsData>({
      activePage: 0,
      filterData: { texto: "", categoria: null },
    });

  const getTarjetas = useCallback(() => {
    const config: AxiosRequestConfig = {
      method: "GET",
      url: "/tarjetas",
      withCredentials: true,
      params: {
        page: controlComponentsData.activePage,
        size: 3,
        categoriaId: controlComponentsData.filterData.categoria?.id,
        texto: controlComponentsData.filterData.texto,
      },
    };

    requestBackend(config).then((response) => {
      setPage(response.data);
    });
  }, [controlComponentsData]);

  useEffect(() => {
    getTarjetas();
  }, [getTarjetas]);

  const handlePageChange = (pageNumber: number) => {
    setControlComponentsData({
      activePage: pageNumber,
      filterData: controlComponentsData.filterData,
    });
  };

  const handleSubmitFilter = (data: TarjetaFilterData) => {
    setControlComponentsData({
      activePage: 0,
      filterData: data,
    });
  };

  return (
    <>
      {!categorias.length ? (
        <h1>Debe crear una categoria</h1>
      ) : (
        <>
          <TarjetaFilter onSubmitFilter={handleSubmitFilter} />
          <div>
            {page?.content.map((tarjeta) => (
              <div key={tarjeta.id}>
                <TarjetaCard tarjeta={tarjeta} onDelete={getTarjetas} />
              </div>
            ))}
          </div>
          <Pagination
            pageCount={page ? page?.totalPages : 0}
            range={2}
            onChange={handlePageChange}
          />
        </>
      )}
    </>
  );
};

Solution

  • Your categories categorias are initially empty. Until you load them, you will be displaying the text.

    The simplest possible solution is not to initialise categorias to empty list but to null:

    const [categorias, setCategorias] = useState<Categoria[] | null>(null);
    

    Then you can add an additional rendering state until categorias are loaded:

    if (!categorias) {
       return <h1>Cargando…</h1>
    }
    
    return (
        <>
          {!categorias.length ? (
            <h1>Debe crear una categoria</h1>
          ) : (
        ...
    
    

    However, ideally you would handle loading state using complex structures similar to:

    { isLoading: true, error: null, categorias: [] }
    

    Then you would be able to correctly distinguish when to display loading indication and when to display loading error.