I hope you can help me with this problem I have.
I have a store like this:
list: {},
status: ''
When I enter to this module a saga will be fired. This saga will request the list to the server and will modify the status. First, it'll modify status to LOADING, this will do that my component renders a spinner. When I receive the information, my status will be SUCCESS, my list will have the information and my component will render the information.
list: { ... },
status: SUCCESS
When I leave this module and enter again, all this process will be executed and status will be change to LOADING and then to SUCCESS causing my component to render the spinner. What I want to do is check if my list is empty or not. If my list is empty I want that the status change to LOADING, but if it's not, I don't want that the status change to LOADING.
function* fetchProfessionals() {
try {
/*
if list is empty {
yield put(actions.professionalsStatus(LOADING));
}
*/
const { data, isError } = yield call(fetchProfessionalsApi.run);
if (isError) throw new Error();
yield put(actions.professionalsFetched(data));
} catch (e) {
yield put(actions.professionalsStatus(ERROR));
} finally {
if (yield cancelled()) {
yield put(actions.professionalsStatus(CANCELED));
yield call(fetchProfessionalsApi.cancel);
} else {
yield put(actions.professionalsStatus(SUCCESS));
}
}
};
In this case I have to access to the store using select or is there a better alternative?
Thank you.
I'd either use select to inspect the store for the desired data or in the reducer, when the list changes, you can update a isLoading property in the state based on list length. I'd probably prefer to dispatch an explicit LOADING action to make updates explicit, but it depends on your preference and the situation.
I'm having issues with this as well.
The docs are a bit ambiguous on what is actually wrong with using select, and if select is the problem at all. They state that:
Preferably, a Saga should be autonomous and should not depend on the Store's state.
Which is true, tying a single saga to your whole state structure is bad, you'd need to refactor all the time. But it was bad for connected components already, before redux-saga existed. That's why selectors are so useful, and indeed the docs immediately launch into an example implementation of a getCart selector, and how:
The Saga is coupled only with the getCart selector. If we have many Sagas (or React Components) that needs to access the cart slice, they will all be coupled to the same function getCart. And if we now change the state shape, we need only to update getCart.
Which is also true.
but where does that leave us with select? If we are now only coupled to the selector, is it still preferable to not use select? If so, why? it has the same amount of surface area to refactoring as a connected component that consumes the same resources.
I'm currently working on an application that uses JSON-API and redux saga, with a paginated list of objects. deep in the component hierarchy, there is an infinite scroll component that needs to trigger a saga to get more objects. let's say the component hierarchy looks like this:
<Application>
<Page>
<ObjectListContainer>
<DetailsPane />
<RelationshipPane>
<InfiniteScrollContainer>
<list>
<object key="object1">
<object key="object2">
</list>
<infiniteScrollTrigger />
</InfiniteScrollContainer>
</RelationshipPane>
</ObjectListContainer>
</Page>
</Application>
Obviously very simplified.
Currently, InfiniteScrollTrigger will trigger the getMoreObjects saga when it comes into view. that saga uses select to grab the url for the next page of objects saved in state, queries that url for more objects, and uses a put call to append them to the list of objects, refreshing the list and starting the cycle anew. According to one of my work buddies, this is an anti-pattern, since he doesn't use select in his sagas at all.
Let's say I want to refactor my saga so that I don't use select. ObjectListContainer already has a reference to the list object to display data about it, so I just get the url from there and pass it down. Is my application really better if I ferry the url all the way down to the InfiniteScrollTrigger? ObjectListContainer, RelationshipPane, and InfiniteScrollContainer all don't care about the url (however it is required, so it will need to be in their PropTypes) and InfiniteScrollTrigger only uses it to dispatch to the saga.
Instead, I refactor to connect the InfiniteScrollTrigger, pulling the url in directly. Now I'm not passing it down, but is my application still any better? I've made a static component now re-render any time we fetch more objects, and if the underlying state changes in a way that causes getMoreObjects to change I still have to refactor, I just do it in a different place. Plus, what if the saga needs _a lot_ of state instead of just a single url? now I've got this throwaway component connected to the store that knows about all these things it shouldn't, just to make it so that a redux-saga doesn't have to use select. I reuse components more than I reuse sagas, so I'd prefer to keep the former generic if I can (though obviously that's not always feasible).
Is there another data management scheme I'm missing that makes avoiding select a clean and viable way to structure your applications? I would love to reduce the complexity of my sagas, but for the reasons outlined above I haven't been able to without introducing needless complexity.
This is what I think about this:
I don't see anything wrong with your approach of using selectin saga (馃憢we have this effect for a reason, right?). This sentence in docs cause more harm than good and we should probably rephrase it somehow (perfect PR opportunity :>)
Most helpful comment
This is what I think about this:
I don't see anything wrong with your approach of using
selectin saga (馃憢we have this effect for a reason, right?). This sentence in docs cause more harm than good and we should probably rephrase it somehow (perfect PR opportunity :>)