Skip to content

Improvement suggestion around dispatching a fetch action from within a functional component and handling errors #186

Open
@dcs3spp

Description

@dcs3spp

Hi,

Firstly, I have found the patterns in the guide very helpful for configuring actions, epics, reducers etc. However, I do not understand how to dispatch a fetch API request action and subsequently bind to a fetch successful action. I have managed to create an epic for a fetch API request that then triggers a success or error action depending upon the success/failure of the API request.

I do not understand how to hook this up to a functional component, as highlighted in the comments of the CourseList functional component code listing below:

CourseList Functional Component

import React, { useEffect } from 'react';

import Grid from '@material-ui/core/Grid';
import { GridSpacing } from '@material-ui/core/Grid';

import Course from '../../components/Course/Course';
import { Course as CourseModel } from '../../redux/features/course/model';

type Props = {
  courses: CourseModel[];
  onFetchCourseRequest: ??? // I want to bind this to the fetch courses action but do not understand how?? what type should this be??
};

export const CourseList: React.FC<Props> = props => {
  const { courses, onFetchCourseRequest } = props;

  // functional equivalent of componentDidMount 
  // this only gets triggered once, since
  // dependency list is empty
  // I want this to to trigger the dispatch of a FETCH_COURSE_LIST action and bind to resulting FETCH_COURSE_LIST
  // I do not understand how to do this from examples....
  useEffect(() => {
    onFetchCourseRequest();
  }, []);

  return (
    <div style={{ marginTop: 20, padding: 30 }}>
      {
        <Grid container spacing={2 as GridSpacing} justify="center">
          {courses.map(element => (
            <Grid item key={element.courseID}>
              <Course course={element} />
            </Grid>
          ))}
        </Grid>
      }
    </div>
  );
};

CourseListConnected

import { RootState } from 'ReduxTypes';

import { connect } from 'react-redux';

import { CourseList } from './CourseList';
import { courseActions, courseSelectors } from '../../redux/features/course';

const mapDispatchToProps = {
  onFetchCoursesRequest: courseActions.fetchCoursesRequest,
};

const mapStateToProps = (state: RootState) => ({
  courses: courseSelectors.getReduxCourses(state.courses.fetchCoursesSuccess),
});

const CourseListConnected = connect(
  mapStateToProps,
  mapDispatchToProps,
)(CourseList);

export default CourseListConnected;

Epic for Fetching a Course from API

import { RootAction, RootState, Services } from 'ReduxTypes';
import { Epic } from 'redux-observable';

import { isOfType } from 'typesafe-actions';

import { of } from 'rxjs';
import {
  catchError,
  filter,
  ignoreElements,
  map,
  switchMap,
} from 'rxjs/operators';

import { fetchCoursesFail, fetchCoursesSuccess } from './actions';

import constants from './constants';

export const fetchCoursesRequestAction: Epic<
  RootAction,
  RootAction,
  RootState,
  Services
> = (action$, state$, { courseServices }) =>
  action$.pipe(
    filter(isOfType(constants.FETCH_COURSES_REQUEST)),
    switchMap(action =>
      courseServices.default.getCourses().pipe(
        map(courses => fetchCoursesSuccess(courses)),
        catchError(error => of(fetchCoursesFail(error))),
      ),
    ),
    ignoreElements(), // ignore everything except complete and error, template does this
  );

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions