Search code examples
javascriptjsonreactjsfetch

React useEffect doesn't trigger fetch when rendering component


I'm trying to fetch data from a JSON and map that data to components, when I comment out the components the fetch successfully retrieves my data (via console log). But when I have the components turned on, the app fails with the error "Cannot read property 'root' of undefined".

I'm guessing this is because the component is rendering before the fetch fires causing it to error out, but I have no idea how to fix it.

Apologies in advance for my lack of knowledge, I just started learning React and am very green.


import "./app.css";
import Legal from "../legal/Legal";
import BlogEntry from "./BlogEntry";

const Blog = ({ setSelectedLink }) => {
  const [items, setItems] = useState({});
  const fetchItems = async () => {
    const data = await fetch("https://api.jsonbin.io/v3/b/60bc11a492164b68bec13b15", {
      method: "GET",
      headers: {
        "X-Master-Key": "<API KEY HIDDEN FOR STACKOVERFLOW>",
      },
    });

    const items = await data.json();
    console.log(items);
    setItems(items);
  };
  useEffect(() => {
    fetchItems();
    window.scroll(0, 0);
    setSelectedLink(1);
  }, []);
  return (
    <>
      <div className="blog-page">
        <div className="blog-container">
          {items.record.root.map((entry) => {
            return (
              <BlogEntry
                key={entry.id}
                id={entry.id}
                title={entry.title}
                date={entry.date}
                body={entry.body}
              />
            );
          })}
        </div>
      </div>
      <Legal />
    </>
  );
};

export default Blog;

Solution

    1. Set a default value for items.record.root
    const [items, setItems] = useState({ record: { root: [] } });
    
    1. Pass false into the dependency array of your effect hook to have it run once (and only once)
      useEffect(() => {
        fetchItems();
        window.scroll(0, 0);
        setSelectedLink(1);
      }, [false]);