Deep down the rabbit hole of state management and server cache

Natalia Tepluhina

Principal Engineer

Core Team Member

Google Dev Expert

@N_Tepluhina

State management is complicated

Pinia

Vue

import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => {
    return {
      counter: 0,
    }
  },
})
export const useStore = defineStore('main', {
  state: () => {
    return {
      counter: 0,
    }
  },
  actions: {
    increment() {
      this.counter++
    },
  },
})

- synchronous

- does not create a lot of boilerplate

- has one source of changes

- non-persistent

"Curiouser and curiouser!"

Alice

Looks simple, right?

export const useStore = defineStore('characters', {
  state: () => {
    return {
      characters: [],
      loading: false,
      error: null,
    }
  },
})
export const useStore = defineStore('characters', {
  state: () => {
    return {
      characters: [],
      loading: false,
      error: null,
    }
  },
  actions: {
    async fetchCharacters() {
      this.loading = true
      try {
        const response = await getCharacters()
        this.characters = response.data
      } catch (error) {
        this.error = error
      }
      this.loading = false
    },
  },
})

Now let's add it to both components!

import { useStore } from '../store'

const store = useStore()
store.fetchCharacters()

Woops

Ok fine let's deduplicate

export const useStore = defineStore('characters', {
  state: () => {
    /* ... */
  },
  actions: {
    /* ... */
  },
  getters: {
    fetchStarted: (state) => state.characters.length > 0 || state.loading,
  }
})
if (!store.fetchStarted) {
  store.fetchCharacters()
}
[
  {
    id: "1",
    name: "Alice",
    image_url: "https://image1.png",
  },
  {
    id: "2",
    name: "The Cheshire Cat",
    image_url: "https://image2.png",
  },
]
[
  {
    id: "1",
    name: "Alice",
    image_url: "https://image1.png",
    occupation: "White Pawn",
    address: "London",
    description: "Alice is a very lovely, pretty and beautiful young girl..."
  },
  {
    id: "2",
    name: "The Cheshire Cat",
    image_url: "https://image2.png",
  },
]

Sounds easy?

actions: {
  async fetchCharacter(id) {
    this.loading = true
    try {
      const response = await getCharacter(id)
      const existingCharacter = this.characters.find((t) => t.id === id)
      if (existingCharacter) {
        const { description, address, occupation } = response.data
        existingCharacter.description = description
        existingCharacter.address = address
        existingCharacter.occupation= occupation
      } else {
        this.characters.push(response.data)
      }
    } catch (error) {
      this.error = error
    }
    this.loading = false
  },
},
actions: {
  async fetchCharacters() {
    this.loading = true
    try {
      const response = await getCharacters()
      this.characters = response.data
    } catch (error) {
      this.error = error
    }
    this.loading = false
  },
},
actions: {
  async fetchCharacters() {
    this.loading = true
    try {
      const response = await getCharacters()
      this.characters = response.data.reduce((acc, current) => {
        const existingCharacter = acc.find((t) => t.id === current.id)
        if (!existingCharacter) {
          return [...acc, current]
        }
        return acc
      }, this.characters)
    } catch (error) {
      this.error = error
    }
    this.loading = false
  },
},

Now we're ok?

Haha no

getters: {
  fetchStarted: (state) => state.characters.length > 0 || state.loading,
}
getters: {
  fetchStarted: (state) => state.characters.length > 1 || state.loading,
}

"If you want to get somewhere else, you must run at least twice as fast as that!"

Red Queen

- how to fetch only when data is not fetched already?

- how to keep my data up-to-date?

- what if two components start fetching simultaneously?

Not all states are born equal!

Global state

Global state

Server cache

Local state

Apollo Client

"The best way to explain it

is to do it"

Dodo

Thank you!

NataliaTepluhina

@N_Tepluhina

Made with Slides.com