docs/guides/usage/local-state/advanced-local-state.md
---
description: Advanced recipes for Apollo Elements to manage local state
---
# Usage >> Local State >> Advanced Local State || 20
<style data-helmet>
figure {
margin-inline: 0;
}
figure pre {
border-start-start-radius: 0 !important;
border-start-end-radius: 0 !important;
}
figcaption {
border-start-start-radius: 6px;
border-start-end-radius: 6px;
background-color: var(--markdown-table-row-odd-background-color);
padding: 4px 6px;
}
</style>
Say your app deals with Networks and Sites. Each network has a list of sites which belongs to it, so you define the field `isInNetwork` on `Site` which takes a network ID as a field argument. You want to develop <dfn><abbr title="Create, Read, Update, Delete">CRUD</abbr></dfn> operations for networks, and have those operations relate to the list of sites as well.
## The Plan - One Way Data Flow
We'll develop an Apollo element which queries the entire list of sites and displays them in a multi-select dropdown. Clicking a button will issue a mutation to create a new network with the selected sites.
The mutation will take a list of string site IDs as its input. In order to get that list of selected sites, we'll define a `selected` client-side state property on the `Site` object. Whenever the user selects a site in the list, we'll update the local state using `writeFragment`, then when the user clicks the *Create Network* button, we'll filter the list of all sites, taking only the ids of those sites which are selected.
<figure aria-label="Sequence Diagram for one-way data flow">
{%- include ./mermaid-local-state.svg -%}
<figcaption class="visually-hidden">
Sequence diagram showing one-way data flow.
1. from Apollo Cache, to create-network element via AllSitesQuery
2. from create-network element to select-item element via Property Assignment
3. then from select-item element back to create-network element via MouseEvent
4. then from create-network element back to Apollo Cache via writeFragment
</figcaption>
</figure>
Let's define a schema for our app. We'll need `Site` and `Network` types, as well as their associated operations.
<figure>
<figcaption>schema.graphql</figcaption>
```graphql copy
type Site {
id: ID
name: String
isInNetwork(networkId: ID): Boolean
}
type Network {
id: ID
name: String
sites: [Site]
}
type Query {
sites: [Site]
}
type Mutation {
createNetwork(sites: [ID]!): Network
}
```
</figure>
<inline-notification type="tip" title="GraphQL Codegen">
The rest of this page assumes we're working in a project that uses [GraphQL code generator](https://www.graphql-code-generator.com/docs/presets/near-operation-file) to convert `.graphql` files to TypeScript sources.
</inline-notification>
We'll also need a query document for the list of sites and a mutation document for the "create network" operation.
<figure>
<figcaption>Sites.query.graphql</figcaption>
```graphql copy
query Sites {
sites {
id
name
selected @client
}
}
```
</figure>
<figure>
<figcaption>CreateNetwork.mutation.graphql</figcaption>
```graphql copy
mutation CreateNetwork($sites: ID[]!) {
createNetwork(sites: $sites) {
id
name
sites {
id
}
}
}
```
</figure>
Then we'll define a component `create-network` which fetches and displays the list of sites. The rendered shadow DOM for the component will look something like this, using a hypothetical `<select-list>` element:
```html
<select-list>
<select-item item-id="1" item-name="Site 1" selected></select-item>
<select-item item-id="2" item-name="Site 2"></select-item>
<select-item item-id="3" item-name="Site 3"></select-item>
</select-list>
```
The `<select-list>` element (hypothetically) fires a `select` event whenever the selected item changes, so we'll attach a listener to keep each site's local state in sync. When our user clicks on the checkboxes in the list of `<select-item>`s, we'll update that `Site`'s client-side `selected @client` field, which in turn will be read to determine whether a site's corresponding `<select-item>` component will be marked selected.
<figure>
<figcaption>selectedSite.fragment.graphql</figcaption>
```graphql copy
fragment siteSelected on Site {
selected @client
}
```
</figure>
Our UI component will use that fragment to update the specific sites selected.
## Create Mutation Component
To create the Network, the user selects some Sites and then clicks a button which issues the `createNetwork` mutation, so let's implement that mutation now.
This mutation requires an input which is a list of site IDs, which we'll get from the cached local state we prepared above.
Then, when the user is ready to create the Network, she clicks the `Create` button, and the component issues the mutation over the network with variables based on the currently selected sites.
<code-tabs collection="libraries" default-tab="lit">
```html tab html
{% include ./_assets/final.html.html %}
```
```ts tab mixins
{% include ./_assets/final.mixins.ts %}
```
```ts tab lit
{% include ./_assets/final.lit.ts %}
```
```ts tab fast
{% include ./_assets/final.fast.ts %}
```
```ts tab haunted
{% include ./_assets/final.haunted.ts %}
```
```tsx tab atomico
{% include ./_assets/final.atomico.tsx %}
```
```ts tab hybrids
{% include ./_assets/final.hybrids.ts %}
```
</code-tabs>
## Update Network Component
This is great for the `/create-network` page, but now we want to implement an 'update network' mutation component at a `update-network/:networkId` route. Now we have to show the same `<select-list>` of Sites, but the `selected` property of each one has to relate only to the specific page the user is viewing it on.
In other words, if a user loads up `/create-network`, selects sites A and B, then loads up `/update-network/:networkId`, they shouldn't see A and B selected on that page, since they're on the page of a specific site. Then, if they select C and D on `/update-network/:networkId` then return to `/create-network`, they should only see A and B selected, not C and D.
To do this, let's define the `<update-network-page>`'s query to pass a `networkId` argument to the client-side selected field
<figure>
<figcaption>UpdateNetwork.mutation.graphql</figcaption>
```graphql copy
query UpdateNetworkPageQuery($networkId: ID!) {
location @client {
params {
networkId @export(as: "networkId")
}
}
sites {
id
name
isInNetwork(networkId: $networkId)
selected(networkId: $networkId)
}
network(networkId: $networkId) {
id
name
}
}
```
</figure>
This query lets us combine a view of all Sites with their relationship to a particular Network.
## The Type Policies
Let's define a `FieldPolicy` for `Site`'s `selected` field which lets us handle both cases: the create page and the update page
```ts copy
const typePolicies: TypePolicies = {
Site: {
fields: {
selected: {
keyArgs: ['networkId'],
read(prev, { args, storage, readField }) {
if (!args?.networkId)
return prev ?? true;
else {
return storage[args.networkId] ?? readField({
typename: 'Site',
fieldName: 'isInNetwork',
args: { networkId: args.networkId }
});
}
},
merge(_, next, { args, storage }) {
if (args?.networkId)
storage[args.networkId] = next;
return next;
},
}
}
}
}
```
With this type policy, any time the `selected` field gets accessed without any args, or with no `networkId` arg, the caller gets the previous known value - a boolean flag on the site object.
But if the field is queried with a `networkId` arg, as in the update-network page, instead of returning the 'global' value (`prev`), it will return the value stored at `storage[args.networkId]`, which is a `Record<string, boolean>`.