Home > Blockchain >  React 18 - Cannot read properties of undefined with axios and redux
React 18 - Cannot read properties of undefined with axios and redux

Time:05-17

I'm trying to get information from an API with redux and axios. But in my component I'm receiving the following error.

enter image description here

I have this in my package.json

"dependencies": {
    "autoprefixer": "^10.4.7",
    "axios": "^0.27.2",
    "postcss": "^8.4.13",
    "prettier": "^2.6.2",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-redux": "^8.0.1",
    "react-scripts": "5.0.1",
    "redux": "^4.2.0",
    "redux-devtools-extension": "^2.13.9",
    "redux-thunk": "^2.4.1",
    "tailwindcss": "^3.0.24",
    "web-vitals": "^2.1.4"
  }

The structure that I'm trying to follow is with Redux and Redux-thunk.

enter image description here

  • Axios folder is the axios client with the API URL
  • Home is the principal component

store.js

import thunk from 'redux-thunk';
import { applyMiddleware, createStore } from 'redux';

import rootReducer from './reducers';
import { composeWithDevTools } from 'redux-devtools-extension';

const initialState = {};

const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  composeWithDevTools(applyMiddleware(...middleware)),
);

export default store;

financesActions.js

import axiosClient from '../../config/axios'; //axios connection with API
import { FINANCES_ERROR, GET_FINANCES } from '../types'; //just constants

export const getFinances = () => async (dispatch) => {
  try {
    const response = await axiosClient.get('/api/finance'); //axiosClient has the rest of the URL and it's working
    dispatch({
      type: GET_FINANCES,
      payload: response.data,
    });
  } catch (error) {
    dispatch({
      type: FINANCES_ERROR,
      payload: console.error(error),
    });
  }
};

financeReducer.js

import { GET_FINANCES } from '../types';

const initialState = {
  finances: [],
};

export const financeReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_FINANCES:
      return {
        ...state,
        finances: action.payload,
      };
    default:
      return state;
  }
};

index inside reducer's folder

import { combineReducers } from 'redux';
import { financeReducer } from './financeReducers';

export default combineReducers({
  finances: financeReducer,
});

The index file from src root has the Provider from react-redux with the store I created:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import { Provider } from 'react-redux';
import store from './store/store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
);

Finally, in the Home component when I'm trying to consume the API and bring the information.

import { connect } from 'react-redux';
import { getFinances } from '../../store/actions/financesActions';
import { useEffect } from 'react';

const Home = () => {
  const { finances } = this.props.finances;

  useEffect(() => {
    this.props.getFinances();
    console.log(finances);
  }, []);

  return (
    <p>Hey</p>
  );
};

const mapStateToProps = (state) => ({ finances: state.finances });

export default connect(mapStateToProps, { getFinances })(Home);

At the end I'm trying to use the connect from react-redux with the props. I had found some code that I followed but with an older version of React. The original code was the following:

import React, { Component } from 'react'
import {connect} from 'react-redux'
import {getUsers} from '../store/actions/usersAction'

 class users extends Component {
    componentDidMount(){
        this.props.getUsers()
        
    }
    render() {
        const {users} = this.props.users
        console.log(users)

        
        return (
            <div>
                {users.map(u => 
                     <React.Fragment key={u.id}>
                         <h6 >{u.name}</h6> 
                     </React.Fragment>
                )}
            </div>
        )
    }
}

const mapStateToProps  = (state) => ({users:state.users})

export default connect(mapStateToProps, {getUsers})(users)

I know the original code is using DidMount and older things from previous React versions. That's why I change that with the useEffect Hook. But something is missing, I think it's something with dispatch functions from the reducer file. Or maybe I can't use those props.

What am I doing wrong?

CodePudding user response:

Home is a functional component, not a class component. You do not require this to access props. They are fed into the component like function parameters. You probably missed that while migrating from class components -

import { getFinances as getFinancesAction } from '../../store/actions/financesActions';

const Home = ({
  finances,
  getFinances,
}) => {

  useEffect(() => {
    getFinances();
  }, []);

  return (
    <p>Hey</p>
  );
};

const mapStateToProps = (state) => ({ finances: state.finances });

export default connect(mapStateToProps, { getFinances: getFinancesAction })(Home); // 

NOTE - I have used an alias while importing getFinances, else the file would have had two functions with the same name.

  • Related