6

I am using same form and same state in redux for add and edit. When I call api for fetching data for edit and we change our router to add form before the response arrives. All the form datas will be auto filled for add item since i am using same state for both add and edit. Is there any way to prevent this from happening?

my action creator:

fetchById: function (entity, id) {
        return function (dispatch) {
            dispatch(apiActions.apiRequest(entity));
            return (apiUtil.fetchById(entity, id).then(function (response) {
                    dispatch(apiActions.apiResponse(entity));
                    dispatch(actions.selectItem(entity, response.body));
            } 
       }
}

As response is late then selectItem is dispatched late. And when I open form for adding item then this form is filled with this data from response.

Clarkie
  • 7,490
  • 9
  • 39
  • 53

2 Answers2

4

Thunks get both dispatch and getState as arguments.

If you keep the current route somewhere in your state, you can check in the API callback whether you’re still on the same page with getState().routing.path or something like this, and return if it has changed since the API call started.

If you don’t keep the current route in the state, you can instead read this.context.router in your component (don’t forget to define contextTypes to get it!) and pass it to the action creator. The action creator can then use router.isActive() to check whether we’re still on the same path before and after the request.

Finally, you can just not use the same state for editing and adding an item. Your state shape can look like { drafts: { newItem: ..., 1: ..., 2: ... } } and you could keep the “add” form in drafts.newItem, and keep any “edit” form by the corresponding entity’s ID. This way they would never overwrite each other and, as a bonus, in-progress edits could be preserved, e.g. if you press “Back” and then go to the form again (depending on whether you want this in your UX of course).

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
  • I dont think checking which route we are on is a good idea. One scenario, If we click on edit for one item then we quickly go back and click edit item of second item. If the response for first request comes later than the second one then the form is filled with false data even if we are in same route. – Bishal Shrestha Apr 17 '16 at 13:28
  • Not sure why that would be the case. I presume that different items have different edit routes, is that not so? – Dan Abramov Apr 17 '16 at 14:10
  • I followed your first approach and for storing route into store, I used react-router-redux. Thanks for responding.. – Bishal Shrestha Apr 18 '16 at 13:03
1

I saw this blog post the other day and I think the second part describes what you are looking for. You don't necessarily want to cancel the requests but ignore the responses.

Basically you want to create an outer reducer which will keep track of your async requests to the server with unique ids. Only if the request id is in the list then allow it into the sub reducers.

When you switch page you will want to clear out this list of unique ids.

Lifted straight from the blog:

const initialState = [];

function update(state = initialState, action) {
  const { seqId } = action;

  if (action.type === constants.UNLOAD) {
    return initialState;
  }
  else if (seqId) {
    let newState;
    if (action.status === 'start') {
      newState = [...state, seqId];
    }
    else if (action.status === 'error' || action.status === 'done') {
      newState = state.filter(id => id !== seqId);
    }

    return newState;
  }

  return state;
}

and then restrict the sub reducers:

let store = createStore((state, action) => {
  if (action.seqId &&
      (action.status === 'done' || action.status === 'error') &&
      state &&
      state.asyncRequests.indexOf(action.seqId) === -1) {
    return state;
  }
  return reducer(state, action);
});

Big shout out to James for this. Really nice solution and very well explained in his blog post.

Clarkie
  • 7,490
  • 9
  • 39
  • 53