Home > Web Front-end > JS Tutorial > Understanding Vue Reactivity with Pinia Stores

Understanding Vue Reactivity with Pinia Stores

Barbara Streisand
Release: 2024-11-20 15:51:14
Original
641 people have browsed it

Understanding Vue Reactivity with Pinia Stores

At my workplace I was being tasked with creating a mock chat store for internal local dev work, and while doing so I made few notes about Vue (I had some experience, but not with hooks), So this is just my obsidian notes, I hope its useful to you :)

Table of Contents

  1. Ref and Reactive References
  2. Watch and Reactivity
  3. Pinia Store Integration
  4. Practical Examples
  5. Best Practices
  6. Common Gotchas

Ref and Reactive References

What is Ref?

ref is Vue's way of making primitive values reactive. It wraps the value in a reactive object with a .value property.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import { ref } from 'vue'

 

// Inside Pinia Store

export const useMyStore = defineStore('my-store', () => {

  // Creates a reactive reference

  const count = ref<number>(0)

 

  // To access or modify:

  function increment() {

    count.value++  // Need .value for refs

  }

 

  return {

    count// When exposed, components can use it without .value

    increment

  }

})

Copy after login

Types of Refs in Stores

1

2

3

4

5

6

7

8

9

10

11

// Simple ref

const isLoading = ref<boolean>(false)

 

// Array ref

const messages = ref<Message[]>([])

 

// Complex object ref

const currentUser = ref<User | null>(null)

 

// Ref with undefined

const selectedId = ref<string | undefined>(undefined)

Copy after login

Watch and Reactivity

Basic Watch Usage

1

2

3

4

5

6

7

8

9

10

import { watch, ref } from 'vue'

 

export const useMyStore = defineStore('my-store', () => {

  const messages = ref<Message[]>([])

 

  // Simple watch

  watch(messages, (newMessages, oldMessages) => {

    console.log('Messages changed:', newMessages)

  })

})

Copy after login

Watch Options

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// Immediate execution

watch(messages, (newMessages) => {

  // This runs immediately and on changes

}, { immediate: true })

 

// Deep watching

watch(messages, (newMessages) => {

  // Detects deep object changes

}, { deep: true })

 

// Multiple sources

watch(

  [messages, selectedId],

  ([newMessages, newId], [oldMessages, oldId]) => {

    // Triggers when either changes

  }

)

Copy after login

Pinia Store Integration

Store Structure with Refs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

export const useMyStore = defineStore('my-store', () => {

  // State

  const items = ref<Item[]>([])

  const isLoading = ref(false)

  const error = ref<Error | null>(null)

 

  // Computed

  const itemCount = computed(() => items.value.length)

 

  // Actions

  const fetchItems = async () => {

    isLoading.value = true

    try {

      items.value = await api.getItems()

    } catch (e) {

      error.value = e as Error

    } finally {

      isLoading.value = false

    }

  }

 

  return {

    items,

    isLoading,

    error,

    itemCount,

    fetchItems

  }

})

Copy after login

Composing Stores

1

2

3

4

5

6

7

8

9

10

11

12

export const useMainStore = defineStore('main-store', () => {

  // Using another store

  const otherStore = useOtherStore()

 

  // Watching other store's state

  watch(

    () => otherStore.someState,

    (newValue) => {

      // React to other store's changes

    }

  )

})

Copy after login

Practical Examples

Auto-refresh Implementation

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

export const useChatStore = defineStore('chat-store', () => {

  const messages = ref<Message[]>([])

  const refreshInterval = ref<number | null>(null)

  const isRefreshing = ref(false)

 

  // Watch for auto-refresh state

  watch(isRefreshing, (shouldRefresh) => {

    if (shouldRefresh) {

      startAutoRefresh()

    } else {

      stopAutoRefresh()

    }

  })

 

  const startAutoRefresh = () => {

    refreshInterval.value = window.setInterval(() => {

      fetchNewMessages()

    }, 5000)

  }

 

  const stopAutoRefresh = () => {

    if (refreshInterval.value) {

      clearInterval(refreshInterval.value)

      refreshInterval.value = null

    }

  }

 

  return {

    messages,

    isRefreshing,

    startAutoRefresh,

    stopAutoRefresh

  }

})

Copy after login

Loading State Management

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

export const useDataStore = defineStore('data-store', () => {

  const data = ref<Data[]>([])

  const isLoading = ref(false)

  const error = ref<Error | null>(null)

 

  // Watch loading state for side effects

  watch(isLoading, (loading) => {

    if (loading) {

      // Show loading indicator

    } else {

      // Hide loading indicator

    }

  })

 

  // Watch for errors

  watch(error, (newError) => {

    if (newError) {

      // Handle error (show notification, etc.)

    }

  })

})

Copy after login

Best Practices

1. Ref Initialisation

1

2

3

4

5

// ❌ Bad

const data = ref()  // Type is 'any'

 

// ✅ Good

const data = ref<string[]>([])  // Explicitly typed

Copy after login

2. Watch Cleanup

1

2

3

4

5

6

7

8

9

10

// ❌ Bad - No cleanup

watch(source, () => {

  const timer = setInterval(() => {}, 1000)

})

 

// ✅ Good - With cleanup

watch(source, () => {

  const timer = setInterval(() => {}, 1000)

  return () => clearInterval(timer)  // Cleanup function

})

Copy after login

3. Computed vs Watch

1

2

3

4

5

6

7

// ❌ Bad - Using watch for derived state

watch(items, (newItems) => {

  itemCount.value = newItems.length

})

 

// ✅ Good - Using computed for derived state

const itemCount = computed(() => items.value.length)

Copy after login

4. Store Organization

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

// ✅ Good store organization

export const useStore = defineStore('store', () => {

  // State refs

  const data = ref<Data[]>([])

  const isLoading = ref(false)

 

  // Computed properties

  const isEmpty = computed(() => data.value.length === 0)

 

  // Watchers

  watch(data, () => {

    // Handle data changes

  })

 

  // Actions

  const fetchData = async () => {

    // Implementation

  }

 

  // Return public interface

  return {

    data,

    isLoading,

    isEmpty,

    fetchData

  }

})

Copy after login

Common Gotchas

  1. Forgetting .value

1

2

3

4

5

6

// ❌ Bad

const count = ref(0)

count++ // Won't work

 

// ✅ Good

count.value++

Copy after login
  1. Watch Timing

1

2

3

4

5

// ❌ Bad - Might miss initial state

watch(source, () => {})

 

// ✅ Good - Catches initial state

watch(source, () => {}, { immediate: true })

Copy after login
  1. Memory Leaks

1

2

3

4

5

6

7

8

9

10

11

// ❌ Bad - No cleanup

const store = useStore()

setInterval(() => {

  store.refresh()

}, 1000)

 

// ✅ Good - With cleanup

const intervalId = setInterval(() => {

  store.refresh()

}, 1000)

onBeforeUnmount(() => clearInterval(intervalId))

Copy after login

Remember: Always consider cleanup, type safety, and proper organization when working with refs and watches in Pinia stores

The above is the detailed content of Understanding Vue Reactivity with Pinia Stores. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template