Replacing Redux with Apollo for local state management - reactjs

I'm pretty new to GraphQL and Apollo but I've been using Redux along with React for the last 3 years. Based on Apollo documentation, they encourage developers to use it as the single source of truth:
We want to be able to access boolean flags and device API results from
multiple components in our app, but don't want to maintain a separate
Redux or MobX store. Ideally, we would like the Apollo cache to be the
single source of truth for all data in our client application
I'm trying to figure out the way to replicate with Apollo what Redux would allow me. In my application, I have "Tags", an array of objects with close to 15 different fields each. They are used on 3 different sections of my app and each section shows specific "Tags" as well as specific fields from the "Tags". Based on that, the way I approach this with Redux is to fetch the "Tags" from my API, and, in the reducer, I create different arrays containing the IDs of the specific "Tags" I need for each section and I also create a Map (id, value) with the original data. It would be something like:
const tags = new Map(); //(tagId, tag) containing all the tags
const sectionATags = []; // array of ids for section A tags
const sectionBTags = []; // array of ids for section B tags
const sectionCTags = []; // array of ids for section C tags
My goal is to replicate the same behavior but, even though they encourage you to manage your local state using Apollo, I'm not sure if what I want to achieve is possible in a simple way, or if it is actually a good practice to do so with Apollo. I've been following this example from the docs and what they mainly do is adding or removing extra fields to the data received from the server by extending the query or mutating the cached data with the #client directive.
At the same time, I understand GraphQL was meant to query the specific data you need, instead of the typical REST request where you would get a big JSON with all the data regardless of whether you need it or not, but it feels like it wouldn't be efficient to do 3 different queries in this case with the specific data I need for each section.
I'm not sure if I'm missing something or maybe Apollo is thought for "simpler local state management". Am I following the right path or shall I keep using Redux with another GraphQL library that just allows me to fetch data without a management layer?

Apollo has normalized cache ... you can query once all [fields required anywhere] data - subsequent/additional queries can be "cache only". No additional requests will be made - it's safe if all required fields already exists.
Converting data structure ...usually not needed, we're working on data [and shape] we need ("not our but backend problem"). You can:
change data shape in resolver [in graphql API wrapping REST],
reshape data in REST-link (direct REST API access from Apollo),
read data once at startup, save converted in local (all following queries reads local)
Common app state can be left in redux - data fetching should be in apollo.

Related

Redux Tool Kit Query: Need same data in different two components but without duplicate requests

I'm new to rtk and rtk query , I'm using rtk query. I have two components I need the same data for each, I do not want to call the function twice in each component to get the same data, is there a way to only call it one time then for better performance?
for example
I'm calling getCartItems in one of those components but I need the same data in the nav bar component to get its count
so I don't want to send the same request again am I right ?
When you query data, redux toolkit query will store those queries inside queries of slice in the redux store:
this is stored in the global state, so anytime you make a request to an endpoint, rtk query first checks this property and if there is same query, it does not add a new query, it de-duplicates (eliminates duplicate) and returns the result of the original query
If you call the same query hook with the same argument in multiple components, only one request will be made. Cache entries are shared internally, that's pretty much the point of RTK Query.
Try it out & take a look at your network devtools :)

How to paginate unpaginated data with SWR

Let's say you have some data that is fetched from an api. It all comes in in one chunk, unpaginated. I am using vercel's SWR library. Is there a way to paginate this data client-side? useSWRInfinite relies on the fact that the link that you provide to it will have the ?page={pageNumber} query.
Not sure if I understood you correctly, but if you have unpaginated data when the request is done, do you mean that you have all the data from the API?
If that is the case, why don't you just write the pagination logic?
So you are already holding all the data in some variable I assume, use that data to create pagination. Create the state variable and just do the math so its value changes based on the selected page, there are multiple examples of that.

With Apollo React client, why you not always need a resolver to resolve #client GraphQL queries?

I'm learning how to use Apollo Client for React and how to manage local state using the cache. From the docs, it's as simple as writing to the cache using cache.writeData and reading from it using a simple query. Their example is
const GET_VISIBILITY_FILTER = gql`
{
visibilityFilter #client
}
`;
I then wrap my JSX in a Query and can read the value fine (in my case loggedIn)
return <Query query={GET_LOGGED_IN}>
{({loading, error, data}) => {
const {loggedIn} = data
I'm curious, though, why I don't need to write a resolver for this to work. Is it because with scalar values if a value exists at the root of an object, that is, here at the top level of the cache, Apollo/GraphQL automatically just grabs that value and sends it to you without a resolver?
What are the limits of this, that is, could you grab arrays at the root level without writing a resolver? Objects? I'm assuming not, as these don't seem to be scalars. But if the data is hard-coded, that is, doesn't require any DB lookup, the answer is still no?
From the docs:
[The #client directive] tells Apollo Client to fetch the field data locally (either from the cache or using a local resolver), instead of sending it to our GraphQL server.
If the directive is present on a field, Apollo will attempt to resolve the field using the provided resolver, or fall back to fetching directly from the cache if one doesn't exist. You can initialize your cache with pretty much any sort of data at the root level (taking care to include __typename fields for objects) and you should be able to fetch it without having to also provide a resolver for it. On the other hand, providing a resolver can provide you with more granular control over what's actually fetched from the cache -- i.e. you could initialize the cache with an array of items, but use a resolver to provide a way to filter or sort them.
There's an import nuance here: Fetching without a resolver only works when there's data in the cache to fetch. That's why it's important to provide initial state for these fields when building your client. If you have a more deeply nested #client field (for example, maybe you're including additional information alongside data fetched from the server), you also technically don't have to write a resolver. But we typically do write them because there is no existing data in the cache for those nested fields.
In addition to a great (as usual) answer from Daniel, I would like to add a few words.
You can use (read/write) objects to cache and manipulate its properties directly.
Using resolvers and mutations for local data can help with readability, data access/change unification, overal manageability or future changes (move feature/settings to server).
More practical/advanced example of local state managament you can find in apollo-universal-starter-kit project.

Storing multi-dimensional data in Redux

First off, I'm still a relative newbie to the world of React & Redux. I'm reading about Normalizing State Shape, and their examples are about storing data by ID. But what if I have data that is keyed on multiple dimensions?
For example, my app is displaying cost data for a given Service ID, which is retrieved from an API. However, the user is able to select a time range. The start and end timestamps are passed to the API, and the API returns the aggregated data over that time range. I want to be able to store all the different time period data in Redux so if the user goes back to a previous time period, that data is already there (our API is slow, so having already loaded data available is critical to user experience).
So not only do I have data keyed by Service ID, but also by Start Time / End Time. Since Redux recommends flat data structures, I wonder how flat should I make it? Because normally, I would store the data like this:
{
costData: {
[service_id]: {
[start_time]: {
[end_time]: {
/* data */
}
}
}
}
}
But that seems to go about the idea of flattening the data. One of my ideas was to generate an ID based on Service ID & Start Time & End Time of the form:
<ServiceID>::<StartTime>::<EndTime>
eg.
00123::1505423419::1505785502
So the data is fairly flat:
{
costData: {
'00123::1505423419::1505785502': {
/* data */
}
}
}
The component can generate this ID and pass it to the fetchCostData() action, which can dispatch and fetch the data and store that data on that generated ID. But I don't know if that's the best approach. Are there any guidelines or recommendations on how to approach this?
I recommend using selectors (Reselect) for this nested data, if you're not comfortable with modifying your api.
-> Selectors are the best approach for computing derived data, allowing Redux to store the minimal possible state.
-> Selectors are efficient. A selector is not recomputed unless one of its arguments change.
-> Selectors are composable. They can be used as input to other selectors.
In addition to the other answer, you may want to read the article Advanced Redux Entity Normalization, which describes ways to track additional lookup descriptions of normalized data. I also have some other articles on normalization in the Redux Techniques#Selectors and Normalization section of my React/Redux links list.

Calling setVariables is forcing a refetch from the server instead of using the cached results?

I am attempting to render a component multiple times in a table (i.e. one instance of the component per table row). The component requires a Relay variable to be set to determine how to render the contents of the component.
I am setting this variable via a call to this.props.relay.setVariables in the component's componentWillMount lifecycle event. In my particular case, each of the components end up needing the same set of data from the server so I would expect that the first component to call this.props.relay.setVariables would result in a new fetch from the server but each subsequent component in the table would simply use the results of the fetch from the first component since the generated query is exactly the same for each component in the table.
However, what actually happens is that a new fetch is made for each component in the table which is terrible for performance. Is there some way to tell Relay to reuse the original fetch? I thought it was supposed to do this automatically by caching the queries.
FYI... the query is going through the node interface and the fragment type is an interface graph type.
It doesn't matter at all if query structure or data is the same for each item. Items are cached by IDs and if IDs are different (node interface requires IDs as we know), Relay requires data for each ID.
Why IDs? Because that enables data flattening and results in much efficient and granular caching. It also lets Relay to make efficient diffing which means that only the missing fields per ID are requested.
If in your particular case each of the components end up needing the same set of data, consider using a single Relay Container for parent component and pass the same data to each row. Problem solved!

Resources