How to Show Multiple Records from Different API Calls in React Redux?

I am trying to fetch two distinct records from separate API calls and display them on a single page using React and Redux. When I successfully retrieve “Record 1” and “Record 2” separately, everything functions correctly; however, I encounter an error when attempting to display both records simultaneously. The error I receive is:

TypeError: Cannot read property 'items2' of undefined
    at RecordPage.render

Record 1 works well when fetched individually:

import React from 'react';
import { connect } from 'react-redux';
import { userActions } from '../_actions';

class RecordPage extends React.Component {
    constructor(props) {
        super(props);
        this.state = { us: 0 };
    }

    componentDidMount() {
        this.props.dispatch(userActions.getAll_Record1());
    }

    render() {
        const { recs1 } = this.props;
        return (
            <div style={{ background: 'red' }} className="well col-md-6 col-md-offset-3">
                {recs1.items1 && (
                    <ul>
                        <h1>First Records</h1>
                        {recs1.items1.map((rec1) => <li key={rec1.id}>{rec1.name + ' ' + rec1.id}</li>)}
                    </ul>
                )}
            </div>
        );
    }
}

function mapStateToProps(state) {
    return { recs1: state.recs1 };
}

const connectedRecordPage = connect(mapStateToProps)(RecordPage);
export { connectedRecordPage as RecordPage };  

Record 2 functions correctly when fetched separately:

import React from 'react';
import { connect } from 'react-redux';
import { userActions } from '../_actions';

class RecordPage extends React.Component {
    constructor(props) {
        super(props);
        this.state = { us: 0 };
    }

    componentDidMount() {
        this.props.dispatch(userActions.getAll_Record2());
    }

    render() {
        const { recs2 } = this.props;
        return (
            <div style={{ background: 'red' }} className="well col-md-6 col-md-offset-3">
                {recs2.items2 && (
                    <ul>
                        <h1>Second Records</h1>
                        {recs2.items2.map((rec2) => <li key={rec2.id}>{rec2.email + ' ' + rec2.id}</li>)}
                    </ul>
                )}
            </div>
        );
    }
}

function mapStateToProps(state) {
    return { recs2: state.recs2 };
}

const connectedRecordPage = connect(mapStateToProps)(RecordPage);
export { connectedRecordPage as RecordPage };  

Here’s the code I wrote to fetch both records simultaneously:

import React from 'react';
import { connect } from 'react-redux';
import { userActions } from '../_actions';

class RecordPage extends React.Component {
    componentDidMount() {
        this.props.dispatch(userActions.getAll_Record1());
        this.props.dispatch(userActions.getAll_Record2());
    }

    render() {
        const { recs1, recs2 } = this.props;
        return (
            <div style={{ background: 'red' }} className="well col-md-6 col-md-offset-3">
                {recs1.items1 && (
                    <ul>
                        <h1>First Records</h1>
                        {recs1.items1.map((rec1) => <li key={rec1.id}>{rec1.name + ' ' + rec1.id}</li>)}
                    </ul>
                )}

                {recs2.items2 && (
                    <ul>
                        <h1>Second Records</h1>
                        {recs2.items2.map((rec2) => <li key={rec2.id}>{rec2.email + ' ' + rec2.id}</li>)}
                    </ul>
                )}
            </div>
        );
    }
}

function mapStateToProps(state) {
    return { recs1: state.recs1, recs2: state.recs2 };
}

const connectedRecordPage = connect(mapStateToProps)(RecordPage);
export { connectedRecordPage as RecordPage };  

I’m uncertain if the issue stems from my componentDidMount() method or the mapStateToProps() function.

The problem you are facing arises because the component expects both recs1 and recs2 to be available in the props simultaneously. If one of them is not resolved or doesn't exist in the Redux state, trying to access properties like items2 might result in a TypeError as you've experienced.

Here's a potential solution to handle this scenario more gracefully:

  • Ensure that both data fetch actions are dispatched, and their corresponding reducers are correctly updating the state when data is returned.
  • Ensure that the default state in your reducers for recs1 and recs2 are defined, so they're not undefined at the start.
  • Adjust the rendering logic to handle scenarios where the data might not yet be available.

Here is how you can enhance your code:

// Example of a reducer default state
const initialState = {
    items1: [], // Assuming items are stored in arrays
    items2: []
};

// Enhanced rendering logic
render() {
    const { recs1, recs2 } = this.props;
    return (
        <div style={{ background: 'red' }} className="well col-md-6 col-md-offset-3">
            {recs1 && recs1.items1 ? (
                <ul>
                    <h1>First Records</h1>
                    {recs1.items1.map((rec1) => (
                        <li key={rec1.id}>{rec1.name + ' ' + rec1.id}</li>
                    ))}
                </ul>
            ) : <p>Loading First Records...</p>}

            {recs2 && recs2.items2 ? (
                <ul>
                    <h1>Second Records</h1>
                    {recs2.items2.map((rec2) => (
                        <li key={rec2.id}>{rec2.email + ' ' + rec2.id}</li>
                    ))}
                </ul>
            ) : <p>Loading Second Records...</p>}
        </div>
    );
}

In this example, I've added conditional rendering with fallback messages for both data sets. Additionally, ensure the Redux reducers for both records handle their respective actions and return default states, ensuring they are not undefined from the start.

Hi DancingFox,

To display both records simultaneously without encountering errors, follow these actionable steps to ensure your component handles incomplete data gracefully.

  • Initialize Default State: Update your reducers to provide a default state. This prevents 'undefined' errors when the data isn't loaded yet.
  • Ensure Action and Reducer Alignment: Verify that your Redux setup correctly dispatches actions and updates state on data fetching completion.
  • Implement Conditional Rendering: Check if both records have been loaded before attempting to display them to avoid rendering errors.

Here's an updated example of the recordPage component to guide you:

const initialState = {
  items1: [],
  items2: []
};

// Render with conditional logic
render() {
  const { recs1, recs2 } = this.props;
  return (
    <div style={{ background: 'red' }} className="well col-md-6 col-md-offset-3">
      {recs1 && recs1.items1 ? (
        <ul>
          <h1>First Records</h1>
          {recs1.items1.map((rec1) => (
            <li key={rec1.id}>{rec1.name + ' ' + rec1.id}</li>
          ))}
        </ul>
      ) : <p>Loading First Records...</p>}

      {recs2 && recs2.items2 ? (
        <ul>
          <h1>Second Records</h1>
          {recs2.items2.map((rec2) => (
            <li key={rec2.id}>{rec2.email + ' ' + rec2.id}</li>
          ))}
        </ul>
      ) : <p>Loading Second Records...</p>}
    </div>
  );
}

With these steps, you should efficiently handle dual API responses in React with Redux without encountering undefined errors. This not only optimizes your workflow but ensures a smooth user experience. Let me know if you have further questions!

Another approach you could consider to efficiently handle data from multiple API calls in React Redux involves ensuring that your Redux state is properly structured and your component is designed to accommodate asynchronous data loading.

Here's a concise breakdown of how you can improve your current setup:

  • Consolidate Calls in Redux Thunks or Sagas: Instead of dispatching API calls separately, consider using thunks or sagas to fetch data concurrently and streamline action handling. This can prevent race conditions and make your state management cleaner.
  • Encapsulate State Defaults: Even when errors occur, having a clearly defined initial state in your reducers helps ensure your UI isn't waiting for undefined data. This might already be suggested, but it's crucial.
  • Graceful Fallbacks and Loaders: Enhance user experience by providing user feedback when data is loading. Display fallback UI elements to indicate ongoing data fetch processes.

Here is an enhanced version of the rendering logic that integrates these improvements:

// Example reducer state
const initialState = {
  items1: [],
  items2: []
};

// Updated render logic with loaders
render() {
  const { recs1, recs2 } = this.props;
  return (
    <div className="well col-md-6 col-md-offset-3">
      {recs1 ? (
        <ul>
          <h1>First Records</h1>
          {recs1.items1.map((rec1) => (
            <li key={rec1.id}>{rec1.name + ' ' + rec1.id}</li>
          ))}
        </ul>
      ) : <p>Loading First Records...</p>}

      {recs2 ? (
        <ul>
          <h1>Second Records</h1>
          {recs2.items2.map((rec2) => (
            <li key={rec2.id}>{rec2.email + ' ' + rec2.id}</li>
          ))}
        </ul>
      ) : <p>Loading Second Records...</p>}
    </div>
  );
}

Deploying these changes will support the seamless integration of APIs in your React Redux application, improving efficiency and user experience. If you need further clarification on any of these points, feel free to ask!