0

I'm trying to chain two calls in a single action using a thunk, but it doesn't seem to work as expected. I need the ID value from the first action to call the second one.

Actions look like this:

export const getParentRecords = filterId => {
    return (dispatch, getState) => {
        let headers = {
            filter_id: filterId
        };
        const request = axios({
            method: "GET",
            url: `https://myapi.com/v1/parent-records`,
            headers: headers
        });
        dispatch({
            type: GET_PARENT_RECORDS,
            payload: request
        });
    };
};

export const getChildRecords = (parentId = null) => {
    let url = `https://myapi.com/v1/child-records`;
    if (parentId) {
        url = `https://myapi.com/v1/child-records/?parent_id=${parentId}`;
    }
    return (dispatch, getState) => {
        let headers = {
            //etc...
        };
        const request = axios({
            method: "GET",
            url: url,
            headers: headers
        });
        dispatch({
            type: GET_CHILD_RECORDS,
            payload: request
        });
    };
};

export const getAllRecords = filterId => {
    return (dispatch, getState) => {
        dispatch(getParentRecords(filterId);
        let { parentRecords } = getState();
        let defaultParent = parentRecords.filter(p => p.is_default === true)[0];
        dispatch(getChildRecords(defaultParent.parent_id));
    };
};

In calling component:

const mapStateToProps = state => {
    return {
        parentRecords: state.parentRecords,
        childRecords: state.childRecords
    };
};

export default connect(mapStateToProps, { getAllRecords })(MyComponent);

Problem is; dispatching the first action doesn't seem to be doing anything. When I call getState() afterwards, the data isn't there. The parentRecords variable in getAllRecords is always empty.

I'm really not sure what to do with this. Pretty common scenario but haven't found a way through it.

2 Answers 2

2

I suggest you to use another library for side-effects handling, like redux-saga or redux-observable, since redux-thunk is very primitive.

Redux-saga is generator-based and imperative. Redux-observable is RxJS-based and declarative. So choose whatever you like more.

https://redux-saga.js.org/

https://redux-observable.js.org/


Each asynchronous action should have three action types, eg: GET_CHILD_RECORDS, GET_CHILD_RECORDS_SUCCESS and GET_CHILD_RECORDS_FAILURE.

Using redux-saga it will look like this:

Action creators:

const getChildRecords = (parentId = null) => ({
  type: GET_PARENT_RECORDS,
  payload: parentId
});

Then you can handle this action in saga generator:

function rootSaga*() {
  yield takeLatest(GET_PARENT_RECORDS, onGetParentRecords);
  yield takeLatest(GET_PARENT_RECORDS_SUCCESS, onGetChildRecords);
}

function onGetParentRecords*({ payload: parentId }) {
  try {
    const parentRecords = yield call(apiCallFunctionHere, parentId);
    yield put({
      type: GET_PARENT_RECORDS_SUCCESS,
      payload: parentRecords
    });
  } catch(error) {
    yield put({
      type: GET_PARENT_RECORDS_FAILURE,
      error
    });
  }
}

function onGetChildRecords*({ payload: parentRecords }) {
  const defaultParent = parentRecords.filter(p => p.is_default === true)[0];
  try {
    const childRecords = call(apiFunctionToFetchChildRecords, defaultParent);
    yield put({
      type: GET_CHILDREN_RECORDS_SUCCESS,
      payload: parentRecords
    });
  } catch(error) {
    yield put({
      type: GET_CHILDREN_RECORDS_FAILURE,
      error
    });
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Interesting...I've seen these recommended elsewhere but would like to avoid another framework for something so simple. Check out the answer I posted. I only have a year or so of React & Redux so feedback is helpful. Thanks!
Thank you Yuriy Yakym, I have the same requirement as @Tsar Bomba described and it works perfectly in redux-saga as you explained
0

I'm not interested in introducing yet another framework for something so simple. After the commute home, an idea struck me. Please let me know the pros/cons.

A new getAllRecords function:

export const getAllRecords = filterId => {
    return (dispatch, getState) => {
        let headers = {
            // etc...
        };
        const request = axios({
            method: "GET",
            url: `https://myapi.com/v1/parent-records`,
            headers: headers
        });
        request.then(result => {
            if (result.status === 200) {
                let parentRecords = result.data.payload;
                let defaultParent = parentRecords.filter(p => p.is_default === true)[0];
                dispatch({
                    type: GET_PARENT_RECORDS,
                    payload: parentRecords
                });
                dispatch(getChildRecords(defaultParent.parent_id));
            }
        });
    };
};

This seems to get me everything I need. Gets parent record(s) by executing the promise, dispatches parent and child results.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.