skip to Main Content

I’m having an issue having my state work with a toggle to switch between categories. As far as I understand it I should be using the useEffect hook to have the page re render dependent on conditions. My ‘novelListSelection’ variable is coming out as undefined every time.

What I’m seeking is to click a toggle button. My state for novelListSelector to change which is working. But depending on what state it is in it will render a different set of novels or graphic novels. Here’s the code:

import { ImageBackground, SafeAreaView, StyleSheet } from "react-native";
import { useEffect, useState } from "react";

import { NOVELS } from "../data/dummy-data";
import NovelList from "../components/Novels/NovelList";
import NovelToggle from "../components/Novels/NovelToggle";
import NovelModal from "../components/Novels/NovelModal";

const Canon = () => {
  const [summaryModalOpen, setSummaryModalOpen] = useState({
    isOpen: false,
    novelId: null,
  });

  const [novelListSelector, setNovelListSelector] = useState("novels");

  const canonNovels = NOVELS.filter((novel) => {
    return novel.is_graphic_novel === false && novel.is_canon === true;
  });

  const canonGraphicNovels = NOVELS.filter((novel) => {
return novel.is_graphic_novel === true && novel.is_canon === true;
  });

  let novelListSelection;

  useEffect(() => {
    if (novelListSelector === "novels") {
      novelListSelection = canonNovels;
    } else if (novelListSelector === "graphic-novels") {
      novelListSelection = canonGraphicNovels;
    } else {
      novelListSelection = canonNovels.concat(canonGraphicNovels);
    }
  }, [canonNovels, canonGraphicNovels, novelListSelector]);

  let pageContents;

  if (summaryModalOpen.isOpen) {
    console.log("SUMMARY MODAL OPEN");
    const selectedNovel = NOVELS.filter((novel) => {
      return novel.id === summaryModalOpen.novelId;
    })[0];

    const novelModalProps = {
      id: selectedNovel.id,
      title: selectedNovel.title,
      authors: selectedNovel.authors,
      releaseDate: selectedNovel.release_date,
      summary: selectedNovel.summary,
      image: selectedNovel.image,
      isGraphicNovel: selectedNovel.is_graphic_novel,
      isCanon: selectedNovel.is_canon,
    };

    pageContents = (
      <NovelModal
        {...novelModalProps}
        setSummaryModalOpen={setSummaryModalOpen}
      />
    );
  } else {
    console.log(`NOVEL SELECTION: ${novelListSelection}`);

    pageContents = (
      <>
        <NovelToggle setNovelListSelector={setNovelListSelector} />
        <NovelList
          novels={novelListSelection}
          setSummaryModalOpen={setSummaryModalOpen}
        />
      </>
    );
  }

  return (
    <SafeAreaView style={styles.wrapper}>
      <ImageBackground
        source={require("../assets/images/jedi_holocron.png")}
        style={styles.image}
      >
        {pageContents}
      </ImageBackground>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  wrapper: {
    flex: 1,
    justifyContent: "center",
  },
  image: {
    flex: 1,
  },
});

export default Canon;

Is it my dependencies I’m using in useEffect that is incorrect? Is useEffect not the proper hook for this? I’ve tried variations on what the useEffect is dependent on. I’ve also tried using a switch statement as opposed to an if block chain. Thanks in advance

2

Answers


  1. Having novelListSelection as a variable doesn’t allow the component to re-render causing it to stay undefined. Recommend moving novelListSelection to a state variable for it to be set and rendered correctly on change.

    Try something like the below in your existing code:

      const [novelListSelection, setNovelListSelection] = useState();
    
      useEffect(() => {
        if (novelListSelector === "novels") {
          setNovelListSelection(canonNovels);
        } else if (novelListSelector === "graphic-novels") {
          setNovelListSelection(canonGraphicNovels);
        } else {
          setNovelListSelection(canonNovels.concat(canonGraphicNovels));
        }
      }, [canonNovels, canonGraphicNovels, novelListSelector, setNovelListSelection]);
    
    Login or Signup to reply.
  2. Is it my dependencies I’m using in useEffect that is incorrect?

    No, the dependencies look fine since they are the external (to the hook) references.

    Is useEffect not the proper hook for this?

    No, the useEffect hook is for issuing side-effects, not computing a derived value. For this you should use the useMemo hook. It’s a bit of an anti-pattern to use a useStateuseEffect coupling, use the useMemo hook instead. When you use a useState/useEffect coupling then you have to wait an additional render cycle to access the computed value. Using useMemo you get it immediately in the current render cycle.

    Example:

    const novelListSelection = React.useMemo(() => {
      if (novelListSelector === "novels") {
        return canonNovels;
      } else if (novelListSelector === "graphic-novels") {
        return canonGraphicNovels;
      } else {
        return canonNovels.concat(canonGraphicNovels);
      }
    }, [canonNovels, canonGraphicNovels, novelListSelector]);
    

    or

    const novelListSelection = React.useMemo(() => {
      switch(novelListSelector) {
        case "novels":
          return canonNovels;
        case "graphic-novels":
          return canonGraphicNovels;
        default:
          return canonNovels.concat(canonGraphicNovels);
      }
    }, [canonNovels, canonGraphicNovels, novelListSelector]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search