Reputation: 3014
I've been able to get by with basic sagas implementation for now but my app is getting a little more complex. I chose sagas for the asynchronous capabilities but seem to have misunderstood how things work.
I have a global search input within my application that needs to make two different api calls (different data objects), but the search input also has it's own loading states based on the search/ status of api calls. Based on this information this is the flow of the application:
GLOBAL_SEARCH_REQUEST
)GLOBAL_SEARCH_REQUEST
kicks off (sets loading to true for the input)the global search request saga
function* globalSearchRequestSaga(action) {
const { query } = action
console.log(`searching subscriptions and users for : ${query}`)
try {
yield put(fetchUsersRequest(query))
// call for the subscriptions (leaving it out for simplicity in this example)
yield put(globalSearchSuccess(query))
} catch (error) {
console.log(`error: ${error}`)
yield put(globalSearchFailure(error.message))
}
}
where the fetch users saga looks like
export function* fetchUsersRequestSaga(action) {
const { query } = action
const path = `${root}/users`
try {
const users = yield axios.get(path, { crossDomain: true })
yield put(fetchUsersSuccess(query, users.data))
} catch (error) {
console.log(`error : ${error}`)
yield put(fetchUsersFailure(query, error.message))
}
}
(very basic)
If I do things this way, there is an issue where the the GLOBAL_SEARCH_SUCCESS
action is executed before the completion of the request for users ( and I imagine the same thing if I added in subscriptions api call as well). One solution I found is if I change the line
yield put(fetchUsersRequest(query))
to
yield call(fetchUsersRequestSaga, fetchUsersRequest(query))
where fetchUsersRequestSaga
is the saga from above, and fetchUsersRequest(query)
is the action creator for fetching users. This causes the asnyc functionality to work, and GLOBAL_SEARCH_SUCCESS
waits for the return of the users (correct behavior).
The only issue with this is that the FETCH_USERS_REQUEST
action is no longer logged to the store.
I am wondering if there is a way to either get this to properly log to the store, or return to my previous implementation with proper blocking on the put(fetchUsersRequest(query))
Upvotes: 3
Views: 7180
Reputation: 39270
It's been a while since I worked with sagas but here is some code that will give you a general idea how to wait for a dispatched action.
The way it works is that when you fetch and want to wait for it to fail or succeed you give the fetch action an id, then you can pass that to the waitFor function while simultaneously dispatch the action.
If you don't want or need to wait for it then you can just dispatch the action without an id and it'll still work:
const addId = (id => fn => (...args) => ({
...fn(...args),
id: id++,
}))(0);
const withId = ({ id }, action) => ({ action, id });
function* waitFor(id) {
const action = yield take('*');
if (action.id === id) {
return action;
}
return waitFor(id);
}
function* globalSearchRequestSaga(action) {
const { query } = action;
console.log(
`searching subscriptions and users for : ${query}`
);
try {
//add id to action (id is unique)
const action = addId(fetchUsersRequest, query);
//dispatch the action and then wait for resulting action
// with the same id
yield put(action);
const result = yield waitFor(action.id);
// call for the subscriptions (leaving it out for simplicity in this example)
yield put(globalSearchSuccess(query));
} catch (error) {
console.log(`error: ${error}`);
yield put(globalSearchFailure(error.message));
}
}
export function* fetchUsersRequestSaga(action) {
const { query } = action;
const path = `${root}/users`;
try {
const users = yield axios.get(path, {
crossDomain: true,
});
yield put(//add original id to success action
withId(action, fetchUsersSuccess(query, users.data))
);
} catch (error) {
console.log(`error : ${error}`);
yield put(
withId(//add original id to fail action
action,
fetchUsersFailure(query, error.message)
)
);
}
}
Upvotes: 0
Reputation: 41893
The put
function is a non-blocking action. It won't wait till the promise/api request resolves.
I would suggest you to just call sagas directly instead of dispatching actions.
try {
yield call(fetchUsersRequestSaga, query);
yield call(globalSearchSaga, query); // or whatever its called
}
call
is a blocking action. It will wait until the request finishes, so both if your calls will execute in proper order.
Upvotes: 3