4

I have successfully gotten my React / Redux app to retrieve data from a REST API back end. I'm using the createAsyncThunk feature of Redux Toolkit, which automatically sets up reducers that are called when the HTTP fetch promise resolves with success or failure.

For this particular endpoint, I'd like the Redux store to reflect an error whenever an HTTP 404 Not Found is encountered. Currently that is not happening. The component shown below always returns "Loaded successfully". How can I make it display "Error" instead?

I understand that fetch doesn't resolve with an error on HTTP 4xx errors, and that I need to check the response code myself and resolve it as a failure. What I don't understand is where or how to do that in the code below. I struggle with understanding async/await conceptually, am new to Redux Toolkit, and the code below is already tweaking my brain pretty hard. Help?

Here is my full code:

features/recipeList/recipeListApi.js

export default async function recipeListApi(localApiKey) {
  const response = await fetch('https://httpstat.us/404');
  const responseJson = await response.json();

  return responseJson;
}

features/recipeList/recipeListSlice.js

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import recipeListApi from "./recipeListApi";

const sliceName = "recipeList";
const initialState = {
  loading: false,
  error: null,
  data: null
};

export const fetchRecipeList = createAsyncThunk("recipeList/fetchRecipeList", async (thunkAPI) => {
  const response = await recipeListApi();
  return JSON.stringify(response);
});

const recipeListSlice = createSlice({
  name: sliceName,
  initialState: initialState,
  extraReducers: {
    [fetchRecipeList.pending]: state => {
      if (!state.loading) {
        state.loading = true;
      }
    },
    [fetchRecipeList.fulfilled]: (state, action) => {
      if (state.loading) {
        state.loading = false;
        state.data = action.payload;
      }
    },
    [fetchRecipeList.rejected]: (state, action) => {
      if (state.loading) {
        state.loading = false;
        state.error = action.payload;
      }
    }
  }
});

export const recipeListReducer = recipeListSlice.reducer;

components/RecipeList.js

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchRecipeList } from '../features/recipeList/recipeListSlice';

export const RecipeList = () => {

    const recipeList = useSelector(state => state.recipeList);
    const dispatch = useDispatch();

    /* Equivalent to componentDidMount() */
    useEffect(() => {
        dispatch(fetchRecipeList());
    }, []);

    return <>

        {recipeList.loading && <h1>Loading</h1>}

        {!recipeList.loading && recipeList.error !== null && <h1>Error</h1>}

        {!recipeList.loading && recipeList.error === null && <h1>Loaded successfully</h1>}

    </>;
}
1
  • Such a well written question :) Too bad there isnt an answer to this one. I am facing the same issue. Commented Nov 5, 2020 at 21:55

2 Answers 2

1

Check if the response had a state of ok - or whatever condition you want to check your response for - and return a rejected promise like so:

export default async function recipeListApi(localApiKey) {
  const response = await fetch('https://httpstat.us/404');

  if(!response.ok) {
    return Promise.reject();
  }

  return await response.json();
}
Sign up to request clarification or add additional context in comments.

Comments

0

if your slice [rejected], the state.error should be receive from action.error

// features/recipeList/recipeListSlice.js
[fetchRecipeList.rejected]: (state, action) => {
      if (state.loading) {
        state.loading = false;
        state.error = action.error;
      }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.