4
votes

I have a react component that makes an AJAX call in componentWillMount and backs the data received in response to a redux store. Here is code

componentWillMount() {
  var self = this;
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
    if (this.readyState === 4 && this.status === 200) {
      console.log(this.responseText);
      var json = JSON.parse(this.responseText);
      var data = {
        json
      };
      self.props.dispatch({
        type: "ADD_Exams",
        data
      });
    }
  };
  xmlhttp.open("GET", "http://127.0.0.1:8000/getExams/", true);
  xmlhttp.send();
}

In the reducer, I am assigning the data received in action to an array defined in the reducer state.

const initialState = {
  exams:[]    
}

const examreducer = (state = initialState, action) => {
  switch (action.type) {
    case "ADD_Exams":
      return {
        ...state,
        exams: [...state.exams, action.data.json]
      };
    default:
      return state;
  }
};

But when I use mapStateToProps to read exams variable I get undefined.

const mapStateToProps = (state) => {
    return {
        exams: state.exams
    }
}

export default connect(mapStateToProps)(Exam); 

I am creating store like this

import { Provider } from "react-redux";
const store = createStore(loginReducer, examReducer);
ReactDOM.render(
  <Provider store={store}>
    <Exam />
  </Provider>,
  document.getElementById("root")
);

registerServiceWorker();

console.log(this.props.exams) prints undefined. What is the problem here?

4
where is the console.log in your code?Evgeny Timoshenko
prints "/static/media/examReducer.d41d8cd9.bin" on console.saim2025
how do you initialize the store?Evgeny Timoshenko
its inside the render function of the componentsaim2025
By calling createStore like this const store = createStore(loginReducer,examReducer);saim2025

4 Answers

11
votes

I spent countless hours finding what was wrong, why my props werent getting any information.

I followed @Romain Pellerin and was able to discovered something

I originally had my mapStateToProps like this:

const mapStateToProps = (state) => {


  return {
      userInfo: state.userInfo,
      loading: state.loading,
      error: state.error
  };

}

Following Romain's answer, i console logged and watched the output of my state object, and found that i had to specify the specific reducer that had the state that i needed to access! Because i had used combineReducers, maybe you have to specify which reducer you want to access in mapStateToProps. Soo i had to change my code to this:

const mapStateToProps = (state) => {
    console.log(state);

    return {
        userInfo: state.THE_SPECIFIC_REDUCER.userInfo,
        loading: state.THE_SPECIFIC_REDUCER.loading,
        error: state.THE_SPECIFIC_REDUCER.error
    };
}

Heres my code where i combine the reducers:

const rootReducer = combineReducers({
  loginReducer,
  THE_SPECIFIC_REDUCER
});

Soo, given that i was trying to get "THE_SPECIFIC_REDUCER" state, i had to specify it in the mapStateToProps like this:

myProp: state.THE_SPECIFIC_REDUCER.theThingIWantToAccess

I hope this works for someone!

11
votes

I ran into the same problem. My mistake was because I was doing wrong export/import of the component.

export:

export class MyComponent extends React.Component
...
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

import:

import { MyComponent } from './MyComponent';

to solve I removed the export on the class and used the default:

import MyComponent from './MyComponent';
4
votes

Can you edit your mapStateToProps like this to see the actual content of your state?

const mapStateToProps = (state) => {
   console.log(state);
    return {
        exams: state.exams
    }
}

I suspect your reducer is not at the root of your reducers. If so, you might need to do state.examreducer.exams. Also, is your component wrapped a in Provider? You need it to access the context (through which your state is accessible).

UPDATE

Make sure to initialize your store before rendering any React component.

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';

const store = createStore(combineReducers({loginReducer, examReducer}), {loginReducer:{}, examReducer:{}});
ReactDOM.render(
  <Provider store={store}>
    <Exam />
  </Provider>,
  document.getElementById('root')
)

Then update your mapStateToProps:

const mapStateToProps = (state) => {
    return {
        exams: state.examReducer.exams || []
    }
}
3
votes

I think there's an issue in how you retrieve data or initialize store. Try this:

import { createStore, combineReducers } from 'redux'
import loginReducer from '../path';
import examReducer from '../path';

const rootStore = combineReducers({
  loginReducer,
  examReducer
})

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <Exam />
  </Provider>,
  document.getElementById("root")
);

and then:

const mapStateToProps = (state) => {
    return {
        exams: state.examReducer.exams
    }
}