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
Having
novelListSelection
as a variable doesn’t allow the component to re-render causing it to stay undefined. Recommend movingnovelListSelection
to a state variable for it to be set and rendered correctly on change.Try something like the below in your existing code:
No, the dependencies look fine since they are the external (to the hook) references.
No, the
useEffect
hook is for issuing side-effects, not computing a derived value. For this you should use theuseMemo
hook. It’s a bit of an anti-pattern to use auseState
–useEffect
coupling, use theuseMemo
hook instead. When you use auseState
/useEffect
coupling then you have to wait an additional render cycle to access the computed value. UsinguseMemo
you get it immediately in the current render cycle.Example:
or