cblnpa
cblnpa

Reputation: 507

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

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

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?

Upvotes: 0

Views: 1281

Answers (1)

himayan
himayan

Reputation: 842

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.

Upvotes: 1

Related Questions