0
votes

I'm currently trying to fetch data from my back end using the following. I'm looking to destructure each event item to an eventIndexItem component, but events remains undefined after useEffect.

import React, { useState, useEffect } from 'react'
import EventIndexItem from './event_index_item'
import 'react-modern-calendar-datepicker/lib/DatePicker.css';
import { Calendar, utils } from 'react-modern-calendar-datepicker';

const EventIndex = ({ searchValue, fetchEvents }) => {
    let today = utils().getToday()
    const [ selectedDay, setSelectedDay ] = useState(today)

    function dateFormatter (selectedDay) {
        return selectedDay.month + " " + selectedDay.day
    }

    let formattedDate = dateFormatter(selectedDay)

    let events
    useEffect (() => {
        events = fetchEvents()
    }, [] )

    function handleEvents () {
        const filterEvents = events.filter(event => {
            let title = event.title
            if ( searchValue === "" || title.includes(searchValue) ) {
                return true
            } else {
                return false
            }
        })

        return filterEvents.map(event => (
            <EventIndexItem 
                key={event.id}
                event={event}
            />
        ))
    }


    return (

        <div className='event-index-container'>
            <div className='event-index-left'>
                <h1 className="event-index-date"> { formattedDate } </h1>
                { handleEvents() }
            </div>
            <div className='event-index-right'>
                <Calendar 
                    calendarClassName="event-index-calendar"
                    value={selectedDay}
                    onChange={setSelectedDay}
                    colorPrimary={'#00a2c7'}
                />
            </div>
        </div>
    )
}

export default EventIndex

Redux container looks like so.

import { connect } from 'react-redux'
import EventIndex from './event_index'
import { fetchEvents } from '../../action/event_actions'

const msp = (state, ownProps) => {
    let searchValue = ownProps.searchValue
    return ({
        events: Object.values(state.entities.events),
        searchValue,
        currentUser: state.entities.users[state.session.id]
    })
}

const mdp = dispatch => {
    return ({
        fetchEvents: () => dispatch(fetchEvents())
    })
}

export default connect (msp, mdp) (EventIndex)

Action / Thunks

import * as EventAPIUtil from '../util/event_api_util';
import * as RSVPApiUtil from '../util/rsvp_api_util';

export const RECEIVE_EVENTS = "RECEIVE_EVENTS";
export const RECEIVE_EVENT = "RECEIVE_EVENT";
export const REMOVE_EVENT = "REMOVE_EVENT";

const receiveEvents = ( events ) => {
    return ({
        type: RECEIVE_EVENTS,
        events
    });
};

export const fetchEvents = () => (dispatch) => (
    EventAPIUtil.fetchEvents().then(events => dispatch(receiveEvents(events)))
);

Reducers

import {
    RECEIVE_EVENTS,
    RECEIVE_EVENT,
    REMOVE_EVENT
} from '../action/event_actions';


const eventsReducer = (state = {}, action) => {
    Object.freeze(state);
    let newState;
    switch (action.type) {
        case RECEIVE_EVENTS:
            return action.events;
        case RECEIVE_EVENT:
            newState = Object.assign({}, state, { [action.event.id]: action.event });
            return newState;
        case REMOVE_EVENT:
            newState = Object.assign({}, state)
            delete newState[action.eventId]
            return newState;
        default:
            return state;
    }
};

export default eventsReducer; 

I've previously created a class component calling this.props.fetchEvents() in componentDidMount and then invoking my handleEvents() in render with no issue. However, the variable events is undefined, but I do see events being added to redux state via redux-logger. Please advise! Also feel free to critique current code as well.

1

1 Answers

2
votes

There are two approaches to this problem. You can either:

  • Fetch from React component (don't recommend)

To do so you will need to do some refactoring to your code

  1. All API call functions should be wrapped in a Promise.
  2. You'll have to add the results to the Redux state through an action dispatch
  3. You'll use async callback function in useEffect
  4. You'll need to wrap the fetch in a conditional to check if values already exist in the state.

Why I don't recommend:

Because you are mixing between business state and UI state. Redux helps separate this by managing the business state in Redux and the UI state in React. So always try avoiding mixing them.

  • Dispatch an async action from Redux (recommended)
    1. Use Redux thunk for async actions.
    2. dispatch the action from useEffect
    3. make the API call from the reducer.
    4. make your conditions in the reducer before the call.

Note that you must set conditions to make the API call and update the state or else the React component will keep re-rendering.

Why I recommend:

It separates business state from UI state.

  • One way for implementation of the recommended approach.

createSlice For auto-generating action creators, Reducer and selectors.

createAsyncThunk For working with API in Redux.

React Redux template Offical template, use as a setup example.