React Apollo with Saga
Is it worth using Apollo with Saga
Long-time ago there was a post — Apollo Client: GraphQL with React and Redux from Sashko Stubailo. He claimed Apollo gave us simple, transparent data flow. And yes… hard to disagree. Nowadays apollo provides super simple hooks
function Dogs({ onDogSelected }) {
const { loading, error, data } = useQuery(GET_DOGS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return (
<select name="dog" onChange={onDogSelected}>
{data.dogs.map(dog => (
<option key={dog.id} value={dog.breed}>
{dog.breed}
</option>
))}
</select>
);
}
You get data directly in your component and don’t care about writing/reading it from the redux state.
I’d separate data fetching from rendering into 2 separate components.
function DogsWithData({ onDogSelected }) {
const { loading, error, data } = useQuery(GET_DOGS);
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return <Dogs dogs=data.dogs />
}
Such separation helps to get rid of data fetching, errors, and loading state in theDog
component. And then I can test Dog
without ApolloProvider. But otherwise, it looks good to me.
So what’s wrong? Why do you need saga?
Although graphql query works pretty nice within React components, mutation is different.
When we start to inject useMutation
in our components as apollo suggests
const [addTodo, { data }] = useMutation(ADD_TODO);
we complicate our components — in practice, you mix visual logic with a functional logic.
The other thing is… your single business operation can include multiple mutations/queries. Even in the simplest scenario when we add a new todo task — we need to do one more thing. After adding a new task I want to refresh the list of the tasks? How can I tell apollo to refresh all tasks? So we can load the newly created task? Of course, there are workarounds… but what am I leading to? In 90% cases when you work with mutation, one line of code is not enough.
const [addTodo, { data }] = useMutation(ADD_TODO);
You need to take care about validation, user/API error handling, post-logic (for example redirect to the next page), etc. And if we go with the default apollo strategy the complexity of our component will grow fast.
What do you propose?
- Be pragmatic — if the logic is complicated — move it to the saga. Saga can be a perfect addition to the apollo.
- Your components should be easily tested. Don’t abuse with decorators. It’s better to keep separate versions of your component.
Dogs
DogsWithData
will give you much better flexibility.