I would have action creators to indicate that a request should be made... I would have sagas watching for the requests and to perform the API calls and dispatch to the store when the API call succeeds/fails.
Each toggle can be set individually without regard for other toggle requests. Your UI can check to see if a particular toggle is pending and then disable just that one toggle...
e.g.
// Actions
const Actions = {
REQUEST: 'REQUEST',
SUCCESS: 'SUCCESS',
FAILURE: 'FAILURE'
};
// Action Creators
const request = id => ({
type: Actions.REQUEST,
payload: id
});
const success = id => ({
type: Actions.SUCCESS,
payload: null,
meta: { id }
});
const failure = (id, err) => ({
type: Actions.SUCCESS,
payload: err,
error: true,
meta: { id }
});
// Sagas
function* requestSaga() {
yield takeEvery(Actions.REQUEST, function*(action) {
const { id } = action.payload;
try {
yield call(yourApiPostMethod, `/api/toggle/${id}`);
}
catch (err) {
yield put(failure(id, err));
return;
}
yield put(success(id));
});
}
// Reducer
const reducer = (state = {}, action) => {
switch (action.type) {
case Actions.REQUEST:
return {
...state,
pending: [ ...state.pending, action.payload ],
};
case Actions.SUCCESS: {
const { id } = action.meta;
const { errors, pending } = state;
const newErrors = { ...errors };
delete newErrors[id];
const i = pending.indexOf(id);
return {
...state,
pending: pending.splice(i, 1),
errors: newErrors,
};
}
case Actions.FAILURE: {
const { id } = action.meta;
const { errors, pending } = state;
const i = pending.indexOf(id);
return {
...state,
pending: pending.splice(i, 1),
errors: { ...errors, [id]: action.payload },
};
}
}
return state;
};
// Components
const Child = ({ toggle, pending, label }) =>
<div onClick={ toggle } disabled={ pending }>{ label }</div>
;
const ParentComponent = ({ toggle, pending, items, ...otherProps }) =>
<div>
{items.map(i =>
<Child
key={ i.id }
toggle={ toggle(i.id) }
label={ i.label }
pending={ pending(i.id) }
/>
)}
</div>
;
const mapStateToParentProps = state => ({
pending: id => state.pending.indexOf(id) >= 0,
});
const mapDispatchToParentProps = dispatch => ({
toggle: id => () => dispatch(request(id))
});
const Parent = connect(
mapStateToParentProps,
mapDispatchToParentProps
)(ParentComponent);
I have a bunch of redux-saga posts planned over the next few weeks on my blog. Check it out if you'd like more of this. https://decembersoft.com/posts/tag/redux-saga/