React Apollo with Saga

Sergey Radzishevskii
2 min readOct 22, 2019

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.

--

--

Sergey Radzishevskii

Enjoy turning complex systems into intelligible architectures chilling in my garden.