1

I've got this action creator :

export function getLevelsMap() {

const request = axios.get(`/api/levels`);

return {
    type: GET_LEVELS_MAP,
    payload: request
    }
}

and this reducer

import { GET_LEVELS_MAP } from '../actions';

export default function (state = null, action) {
switch (action.type) {
    case GET_LEVELS_MAP:
        return action.payload.levels;
    default:
        return state;
}
}

The AJAX request is supposed to return me this :

{
"levels": [
{
  "_id": "5951b7f0600af549fb1d269a",
  "name": "College",
  "__v": 0,
  "subjects": [
    {
      "_id": "594891db1dbdf635ca3019ab",
      "name": "Maths",
      "__v": 0
    },
    {
      "_id": "5948920d1dbdf635ca3019ac",
      "name": "Biology",
      "__v": 0
    }
  ...

And the request does work (I've tested it with PostMan)

Now, I am connecting my component with the reducer and the action :

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

export default connect(mapStateToProps, { getLevelsMap })(ChooseSubject);

I am fetching the data (calling the action) in the componentDidMount method :

  componentDidMount() {
    if (!this.props.levels) {
        this.props.getLevelsMap();
    }
}

and trying to use the reducer :

getSubjects(level) {
    let levels = this.props.levels;

    if (levels) {
        for (var item of levels) {
            if (item.name === level) {
                return item;
            }
        }
    }

    return null;

}

Here is where i state i am using promise middleware

const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
    <BrowserRouter>
        <Switch>
            <Route path="/" component={HomePage} />
        </Switch>
    </BrowserRouter>
</Provider>
, document.querySelector('.root'));

BUT, this.props.levels is undefined ... PS : Everything works if I hardcode (copy paste the result i got from postMan in the subjects_reducer) the answer from the AJAX request.

Thank you very much for your very appreciated help :)

Louis
  • 418
  • 6
  • 22
  • I'm not entirely sure you understand how `Redux` works. You're calling a function that returns the action plainly instead of dispatching it to your store. And if you're using the promise middleware with `axios` your payload will be hidden behind a promise and you won't be able to dot access it – m_callens Jun 27 '17 at 17:14
  • `this.props.level` won't be defined inside a reducer function – Aftab Khan Jun 27 '17 at 17:14
  • @m_callens, thank you for your answer. The promise middleware indeed returns a promise but as soon as the get request gets a responses, it rerenders the component with the data (i think so) – Louis Jun 27 '17 at 17:19

2 Answers2

2

As stated in the earlier answer your axios request is returning a promise and not data therefor what you have is an unresolved promise thus causing your props to not be what you expected.

To answer your question I think you need to not be using redux-promise, but rather redux-thunk. Based on this answer, it seems that redux-promise allows your actions to be promises, but redux-thunk allows your actions to be functions which is what you need. With redux-thunk you will actually be able to resolve the promise and send data down to your reducer, unlike now where your reducer is just getting a promise.

Chaim Friedman
  • 6,124
  • 4
  • 33
  • 61
1

You aren't waiting for axios to complete the call. First, I'm supposing that the promise inside of applyMiddleware is something like redux-thunk, which you will need to do async operations.

Your action creator should look like this:

export const getLevelsMap = () => {
    return function(dispatch) {
        axios.get(`/api/levels`)
            .then(result => {  
                dispatch({
                    type: GET_LEVELS_MAP,
                    payload: result.data
                });
            })
            .catch(err => console.log(err));
    }
}

Or, use async and await:

export const getLevelsMap = () => async dispatch => {
    try {
        let response = await axios.get(`/api/levels`);
        dispatch({
            type: GET_LEVELS_MAP,
            payload: result.data
        })
    } catch (err) {
        console.log(err);
    }
}
whs.bsmith
  • 386
  • 1
  • 2
  • 12
  • Thank you for answering. I've seen this answer already somewhere. But what I don't get is that in the tutorials I followed, they don't use those async operations ... redux-promise is supposed to handle it (i am not usinx redux-thunk but redux-promise). And it should work the way i am trying to do ;) – Louis Jun 27 '17 at 17:48