1

Why does my app not get the latest data from state within useEffect?

I have a component. Article.js that imports the context with useContext.

Codesandbox

import React, { useContext, useEffect } from "react";
import ArticleContext from "../../context/article/articleContext";
import Spinner from "../layout/Spinner";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";

const Article = ({ match }) => {
  const articleContext = useContext(ArticleContext);
  const { getArticle, loading, article } = articleContext;
  useEffect(() => {
    getArticle(match.params.slug);
  }, []);

  /**
   * When i use article anywhere here, it is undefined.
   */

// this will be undefined
console.log(article); 
// Even though getArticle has updated the state via the reducer.


  if (loading) {
    return <Spinner />;
  } else {
    return (
      <main>
        <section className='container'>
          {/* the idea is to put the content here */}
        </section>
      </main>
    );
  }
};

export default Article;

This is my context within a file called articleState.js

import React, { useReducer } from "react";
import ArticleContext from "./articleContext";
import ArticleReducer from "./articleReducer";
import { SET_LOADING, GET_ARTICLE } from "../types";

import client from "../../contentful";

const ArticleState = props => {
  //Initial State
  const initialState = {
    article: {},
    loading: false
  };

  // Set the reducer we want to use
  const [state, dispatch] = useReducer(ArticleReducer, initialState);

  //set up your actions

  //set loading
  const setLoading = () => {
    dispatch({ type: SET_LOADING });
  };

  //get a single article
  const getArticle = async slug => {
    setLoading();
    const res = await client.getEntries({
      content_type: "article",
      "fields.slug": slug
    });
    dispatch({ type: GET_ARTICLE, payload: res.items });
  };

  return (
    <ArticleContext.Provider
      value={{
        article: state.article,
        loading: state.loading,
        getArticle
      }}
    >
      {props.children}
    </ArticleContext.Provider>
  );
};

export default ArticleState;

When checking the chrome developer toolbar, the state successfully updates. I see the article content i pulled down from contentful. It is in my state.

Chrome developer toolbar - React component state

My only issue is when i try to get the data from article it's undefined.

Here's my reducer articleReducer.js

import { SET_LOADING, GET_ARTICLE } from "../types";

export default (state, action) => {
  switch (action.type) {
    case SET_LOADING:
      return {
        ...state,
        loading: true
      };
    case GET_ARTICLE:
      return {
        ...state,
        article: action.payload,
        loading: false
      };
    default:
      return state;
  }
};

Thank you for reading this and for any help.

arkhamDev
  • 1,028
  • 1
  • 15
  • 32
  • Is it possible for you to upload your project to CodeSandbox so people can help you debug? – tombraider Apr 08 '20 at 17:41
  • @tombraider figuring out how to deploy with secrets to codesandbox. I've got a bunch of environment variables. – arkhamDev Apr 08 '20 at 18:06
  • @tombraider - i've added the code to codesandbox - https://codesandbox.io/embed/cool-cdn-5c56t?fontsize=14&hidenavigation=1&theme=dark – arkhamDev Apr 08 '20 at 18:46
  • Well done :) taking a look now – tombraider Apr 08 '20 at 18:56
  • 1
    Your code appears to be working as expected when you navigate to the correct slug? It returns an empty array if no article is found so I've added some error handling around that. Aside from that, everything seems to be A-OK? https://codesandbox.io/s/fancy-cache-i0sdt? – tombraider Apr 08 '20 at 19:09
  • `useEffect(() => { getArticle(match.params.slug); }, [match.params.slug]);` – xadm Apr 09 '20 at 00:22

0 Answers0