0

Hello everyone I'm a beginner currently learning programming, I'm trying to make a webapp that fetches posts from reddit and displays them in a website, I have created an async thunk to fetch the data from a reddit API, when I log it to the console the data is displayed, but whenever I try calling the fetch function and save it to a const instead of returning the object as it normally does, it returns the function.

Picture of the array returned by 'fetchNewPosts()' in reddit.js & the fetch call by 'getPosts() in postSlice.js'

reddit.js API fetch function

export const API_URL = 'https://www.reddit.com';

export const fetchNewPosts = () => async () => {
    const response = await fetch(`${API_URL}/new.json`);
    const json = await response.json();
    console.log(json.data.children.map((post) => post.data))
    const posts = json.data.children.map((post) => post.data)
    return posts
}

Feed.jsx 'fetchNewPosts()' renders an object w/ 25 posts when dispatched in Feed.jsx

import React from 'react';
import {Stats} from '../components/Stats';
import { Post } from '../components/Post/Post'
import { useDispatch, useSelector } from 'react-redux';
import './Feed.css'
import { useEffect } from 'react';
import { getPosts } from '../api/postsSlice';
import { fetchNewPosts } from '../api/reddit';


export const Feed = () => {
  const allInfo = useSelector(state => state.postsReducer);
  const { posts, isLoading, error } = allInfo;
  const dispatch = useDispatch();

useEffect(() => {
  dispatch(getPosts());
  dispatch(fetchNewPosts())
}, [dispatch])

...

postsSlice.js

    import { createSlice } from "@reduxjs/toolkit";
import { fetchNewPosts } from "./reddit";


export const postsSlice = createSlice({
    name: 'postsSlice',
    initialState: {
        posts: [],
        isLoading: false,
        error: false,
        errorMessage: '',
        searchTerm: '',
    },
    reducers: {
        startGetPosts (state) {
            state.isLoading = true;
        },
        getPostsSuccess (state, action) {
            state.isLoading = false;
            state.posts = action.payload;
        },
        getPostsFailed (state, action) {
            state.isLoading = false;
            state.error = true;
            state.errorMessage = action.payload;
        }
    }
})



export const getPosts = () => async (dispatch) => {
    try {
        dispatch(startGetPosts());
        const posts = await fetchNewPosts();
        console.log(posts)
        dispatch(getPostsSuccess());
    } catch (error) {
        dispatch(getPostsFailed(error.message))
    }
}


export const selectPosts = (state) => state.postsSlice.posts;

export const { 
    startGetPosts, 
    getPostsSuccess, 
    getPostsFailed 
} = postsSlice.actions;

export default postsSlice.reducer; 

I appreciate any feedback I can get.

1 Answers1

0

The key thing that you're missing is that the only way to update state in Redux is to "dispatch" an action object that describes "what happened" and contains descriptions/data! Right now your code isn't doing that.

fetchNewPosts looks like this:

export const fetchNewPosts = () => async () => {
    const response = await fetch(`${API_URL}/new.json`);
    const json = await response.json();
    console.log(json.data.children.map((post) => post.data))
    const posts = json.data.children.map((post) => post.data)
    return posts
}

Most of that is okay.

It's got the form of a "thunk action creator", where there's an inner function that can do some async work. That's good.

The first three lines are fine, up through const posts = data.children.map(). You made the request, got the data back, and pulled out what you needed.

However, return posts is wrong. Instead of returning the value, you need to dispatch an action containing the value.

Fortunately, thunk functions are given the Redux dispatch function when they get called. So, you can change it to this:

// Add dispatch as an arg to the inner function
export const fetchNewPosts = () => async (dispatch) => {
    const response = await fetch(`${API_URL}/new.json`);
    const json = await response.json();

    const posts = json.data.children.map((post) => post.data)
    // Dispatch an action containing the data
    // Assume that `somePostsReceivedAction` is defined elsewhere
    dispatch(somePostsReceivedAction(posts));
}

Note that this becomes easier if you use our official Redux Toolkit package. It has a createAsyncThunk API that does let you just make the request and return the data, and it dispatches the actions for you:

const fetchNewPosts2 = createAsyncThunk(
  'posts/fetchNewPosts',
  async () => {
    const response = await fetch(`${API_URL}/new.json`);
    const json = await response.json();
    return son.data.children.map((post) => post.data)
  }
)

See our Redux docs tutorials to better understand what thunks are, how to use them, and how to use Redux Toolkit:

markerikson
  • 63,178
  • 10
  • 141
  • 157