Search code examples
reactjsjsonlocal-storagereact-context

React why does my useEffect hook that retrieves local storage break after wrapping my state in context?


My working code before converting all my state into context:

App.js

function App() {
  const [reptile, setReptile] = useState(function () {
    const storedValue = localStorage.getItem("reptiles");
    return JSON.parse(storedValue);
  })...

...useEffect(
    function () {
      localStorage.setItem("reptiles", JSON.stringify(reptile));
    },
    [reptile]
  );

My current code:

Context.js

const ReptileContext = createContext();

function ReptileProvider({ children }) {
  const [reptile, setReptile] = useState(function () {
    const storedValue = localStorage.getItem("reptiles");
    return JSON.parse(storedValue);
  });

  const [daysFed, setDaysFed] = useState("");
  const [displayForm, setDisplayForm] = useState(false);
  const [displayList, setDisplayList] = useState(true);
  const [displayReptile, setDisplayReptile] = useState(false);
  const [selectedReptile, setSelectedReptile] = useState(null);
  const [today, setToday] = useState(null);
  const [reptileType, setReptileType] = useState("");
  const [reptileName, setReptileName] = useState("");
  const [notes, setNotes] = useState("");

  function handleAddReptile(reptile) {
    setReptile((reptiles) => [...reptiles, reptile]);
  }

  function handleDeleteReptile(reptileId) {
    setReptile((reptiles) => reptiles.filter((rep) => rep.id !== reptileId));
  }

  function handleSelection(reptileId) {
    setSelectedReptile(reptileId);
    setDisplayList(false);
    setDisplayReptile(true);
  }

  function handleBack() {
    setDisplayReptile(false);
    setDisplayList(true);
  }

  return (
    <ReptileContext.Provider
      value={
        (reptile,
        reptileType,
        setReptileType,
        reptileName,
        setReptileName,
        notes,
        setNotes,
        daysFed,
        setDaysFed,
        displayForm,
        setDisplayForm,
        displayList,
        setDisplayList,
        displayReptile,
        setDisplayReptile,
        selectedReptile,
        setSelectedReptile,
        today,
        setToday,
        handleAddReptile,
        handleDeleteReptile,
        handleSelection,
        handleBack)
      }
    >
      {children}
    </ReptileContext.Provider>
  );
}

function useReptile() {
  const context = useContext(ReptileContext);
  if (context === undefined)
    throw new Error("PostContext was used outside of the PostProvider");
  return context;
}

export { ReptileProvider, useReptile };


App.js

function App() {
  const { displayForm, displayList, displayReptile, selectedReptile, reptile } =
    useReptile();

  useEffect(
    function () {
      if (!reptile) return;
      localStorage.setItem("reptiles", JSON.stringify(reptile));
    },
    [reptile]
  );

index.js

 <ReptileProvider>
      <App />
    </ReptileProvider>

I'm receiving an error upon load that reads "JSON.parse: unexpected character at line 1 column 1 of the JSON data". I believe the issue is directly related to where the useEffect hook is placed but I'm not entirely sure. Any help would be greatly appreciated, thanks!


Solution

  • I think the problem is not with the useEffect hook, but with the way you are passing the value to the ReptileContext.Provider. You are using parentheses instead of curly braces, which means you are passing a single expression instead of an object. This might cause the JSON.parse to fail when trying to read the stored value.

    <ReptileContext.Provider value={{ ... }}></ReptileContext.Provider>