誰偷了我的鳳梨!聊聊意外修改 Pinia 資料問題

更新於 2024/08/26閱讀時間約 9 分鐘
鱈魚的魚缸搬家了!新家文章皆有重新修訂,歡迎來新家看看喔。(´▽`ʃ♡ƪ)
raw-image


相信大家用 Vue 3 後應該都改用 Pinia 了吧?沒用過的人趕快試試看吧。(´,,•ω•,,)


Pinia 最簡單的用法就像這樣:

counter.ts

import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useStore = defineStore('counter', () => {
const n = ref(0)

return { n }
})


接著在要使用的地方呼叫 useStore:

App.vue

<script setup lang="ts">
import { useStore } from './counter.ts'

const store = useStore()

function increment() {
store.n++;
}
</script>

<template>
<button @click="increment">
Increment {{ store.n }}
</button>
</template>

其實像這樣直接操作 store.n 的方式,方便歸方便,苓膏龜苓膏


但不是個好做法,這種方式容易讓資料流混亂,想像一下你有多個元件都使用 store.n,然後想改就改。…( ・ิω・ิ)


比較推薦的方式通常為由 store 提供一個修改資料的 function,例如:

counter.ts

import { defineStore } from 'pinia'
import { ref } from 'vue'

interface User {
name: string;
price: number;
}

export const useStore = defineStore('counter', () => {
const user = ref<User>({
name: 'cod',
price: 100,
})

function updateUser(data: Partial<User>) {
user.value = {
...user.value,
...data,
}
}

return {
user,
updateUser,
}
})


呼叫的部份改為:

App.vue

<script setup lang="ts">
import { useStore } from './counter.ts'

const store = useStore()

function increment() {
const price = store.user.price + 1;
store.updateUser({ price });
}
</script>

<template>
<button @click="increment">
price: {{ store.user.price }}
</button>
</template>

這樣如果未來要新增邏輯、權限甚至重構,都容易得多。


但是問題來了,有時候流程中會有「確認」的按鈕,也就是要按下確認後,才修改 store 的資料。


假設有一個負責修改 User 資料的元件:

UserCard.vue

<script setup lang="ts">
import { ref } from 'vue'
import { useStore } from './counter.ts'

const store = useStore();
const user = ref(store.user);

function increment() {
user.value.price++;
}
function cancel() {
user.value = store.user;
}
function submit() {
store.updateUser(user.value);
}
</script>

<template>
<div>
<button class="button" @click="increment">
遞增
</button>
<button class="button" @click="submit">
確認
</button>

<div>
current price: {{ user.price }}
</div>
</div>
</template>

<style>
.button {
margin: 0px 4px;
}
</style>


App.vue

<script setup lang="ts">
import UserCard from './UserCard.vue';

import { useStore } from './counter.ts'

const store = useStore();
</script>

<template>
<div>
store price: {{ store.user.price }}
</div>

<hr />

<UserCard />
</template>

目前畫面如下圖。

raw-image


這時候你會發現出事啦!╭(°A ,°`)╮


按下遞增的時候,不只元件內的 user 數值發生變化,連 store 的數值也一起變啦!Σ(ˊДˋ;)


熟悉 JS 的朋友們一定都知道發生甚麼事,這是因為直接指派物件是 Call by Reference,所以:

const user = ref(store.user);


這個部分的程式會讓 user 依舊指向 store 的 user,結果就意外改到鳳梨裡面的資料了。(›´ω`‹ )


這時候聰明的讀者們一定也想到解法,在 ref 的時候拷貝一次不就行了?


UserCard.vue

<script setup lang="ts">
...
const user = ref(clone(store.user));

function clone<Data>(data: Data): Data {
return JSON.parse(JSON.stringify(data));
}

...
</script>
...

這時候會發現世界恢復和平了,資料一切正常!◝( •ω• )◟


鱈魚:「但是阿。(´● ω ●`)」

路人:「怎麼那麼多但是?…(›´ω`‹ )」


實際上協作開發的時候難保大家都會注意到這件事情,所以保險起見,可以在 Pinia 提供資料時先拷貝一次。


counter.ts

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

interface User {
name: string;
price: number;
}

function clone<Data>(data: Data): Data {
return JSON.parse(JSON.stringify(data));
}

export const useStore = defineStore('counter', () => {
const user = ref<User>({
name: 'cod',
price: 100,
})

function updateUser(data: Partial<User>) {
user.value = {
...user.value,
...data,
}
}

return {
user: computed(() => clone(user.value)),
updateUser,
}
})


這樣即使元件中使用

const user = ref(store.user);

也不會意外修改 Pinia 中的資料了!✧*。٩(ˊᗜˋ*)و✧*。


當然如果使用 immutable.js 這類保證資料不變性的套件也是沒問題。


就看大家喜歡哪一種了。♪( ◜ω◝و(و


以上程式可以來這裡取得:範例程式

avatar-img
17會員
14內容數
各種鱈魚滾鍵盤的雜紀
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
鱈魚的魚缸 的其他內容
到底要用 ref 還是 reactive 是一個很常見的問題,不過現在官方文檔推薦使用 ref 就行,所以也不是甚麼大問題就是了。( •̀ ω •́ )✧ 所以 reactive 真的沒用用途了嗎?這篇文章來記錄一下 reactive 的用法。
watch 不是不能用,而是在使用 watch 之前,先想想有沒有其他方案,真的沒有才用 watch。 千萬不要為了一時方便讓元件裡滿滿的 watch,因為容易產生難以追蹤的副作用,會讓資料流更加複雜。
如果 watch 沒有放在元件最外層,可能會導致元件 onUnmounted 後watch不會自動解除,至於該怎麼辦,就讓我們娓娓道來。( ´ ▽ ` )ノ
大家好,我是鱈魚。(^∀^●)ノシ Vue 3.3 終於新增了泛型元件(Generic Component),這讓我們可以在 TypeScript 環境中得到更準確的型別提示。( •̀ ω •́ )✧ 讓我們一起來看看吧!
大家好,我是鱈魚。(。・∀・)ノ゙ 最近看到大家討論取得 API 時機,有許多人都提到「一定要」或者「習慣」,在 onMounted 這個 hook 內呼叫 API 取得資料。 其實這也沒不好,但是也沒什麼好處就是了。(。・ω・。)
到底要用 ref 還是 reactive 是一個很常見的問題,不過現在官方文檔推薦使用 ref 就行,所以也不是甚麼大問題就是了。( •̀ ω •́ )✧ 所以 reactive 真的沒用用途了嗎?這篇文章來記錄一下 reactive 的用法。
watch 不是不能用,而是在使用 watch 之前,先想想有沒有其他方案,真的沒有才用 watch。 千萬不要為了一時方便讓元件裡滿滿的 watch,因為容易產生難以追蹤的副作用,會讓資料流更加複雜。
如果 watch 沒有放在元件最外層,可能會導致元件 onUnmounted 後watch不會自動解除,至於該怎麼辦,就讓我們娓娓道來。( ´ ▽ ` )ノ
大家好,我是鱈魚。(^∀^●)ノシ Vue 3.3 終於新增了泛型元件(Generic Component),這讓我們可以在 TypeScript 環境中得到更準確的型別提示。( •̀ ω •́ )✧ 讓我們一起來看看吧!
大家好,我是鱈魚。(。・∀・)ノ゙ 最近看到大家討論取得 API 時機,有許多人都提到「一定要」或者「習慣」,在 onMounted 這個 hook 內呼叫 API 取得資料。 其實這也沒不好,但是也沒什麼好處就是了。(。・ω・。)
你可能也想看
Google News 追蹤
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
由於太久沒上Penana即時熱門榜,所以貼出來紀念一下🐰
我的鳳梨小史 有時候,你還真不得不相信,那看似純粹的閱讀經驗,就如一根魔幻的絲線,輕易地就能把你的童年記憶毫不遺漏地串連起來。我們就是依靠這個連結通往記憶之路的彼方。   坦白說,我從小對鳳梨有一種難以言喻的情結,這種情結具體表現如下:例如,果肉慘白、味道強酸,入口咬舌。總而言之,我主觀認為,
Thumbnail
暨花花榜之亂後, 又來一個事件發生.....
詳情如下: https://www.penana.com/article/1436038 希望事情能告一段落,大家能回復昔日的朝氣。 私下我有跟小熊傾談,本人部份的意見獲得接納。還望Penana日後在處理站務上能謹慎成熟一點,別再令不愉快的事情重演。 今後我仍然會使用Penana發表作品,方
Thumbnail
天青花豔蝶雙飛 前情再續苶疲然 誰家小園春常在 念轉時變覆盤難
Thumbnail
2024.05.05~05.06的日記,虛度光陰和漫畫閱讀感想和鳳凰花開的錄音。一些遲鈍的生活,我的報告怎麼還沒做完。
Thumbnail
❗天天出貨 特價免運 小潘鳳梨酥 小潘蛋糕坊 鳳梨酥 鳳凰酥 📌享譽國際,好吃的鳳梨酥及鳳凰酥 ⬇️完整說明網址⬇️ 👉https://shope.ee/6AOZva951Q #Pp貓 #小潘鳳梨酥 #小潘鳳凰酥 #小潘蛋糕坊 #鳳梨酥 #鳳凰酥 #免運 #鳳梨酥推薦 #鳳凰酥推薦 #
Thumbnail
徵的就是你 🫵 超ㄅㄧㄤˋ 獎品搭配超瞎趴的四大主題,等你踹共啦!還有機會獲得經典的「偉士牌樂高」喔!馬上來參加本次的活動吧!
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
由於太久沒上Penana即時熱門榜,所以貼出來紀念一下🐰
我的鳳梨小史 有時候,你還真不得不相信,那看似純粹的閱讀經驗,就如一根魔幻的絲線,輕易地就能把你的童年記憶毫不遺漏地串連起來。我們就是依靠這個連結通往記憶之路的彼方。   坦白說,我從小對鳳梨有一種難以言喻的情結,這種情結具體表現如下:例如,果肉慘白、味道強酸,入口咬舌。總而言之,我主觀認為,
Thumbnail
暨花花榜之亂後, 又來一個事件發生.....
詳情如下: https://www.penana.com/article/1436038 希望事情能告一段落,大家能回復昔日的朝氣。 私下我有跟小熊傾談,本人部份的意見獲得接納。還望Penana日後在處理站務上能謹慎成熟一點,別再令不愉快的事情重演。 今後我仍然會使用Penana發表作品,方
Thumbnail
天青花豔蝶雙飛 前情再續苶疲然 誰家小園春常在 念轉時變覆盤難
Thumbnail
2024.05.05~05.06的日記,虛度光陰和漫畫閱讀感想和鳳凰花開的錄音。一些遲鈍的生活,我的報告怎麼還沒做完。
Thumbnail
❗天天出貨 特價免運 小潘鳳梨酥 小潘蛋糕坊 鳳梨酥 鳳凰酥 📌享譽國際,好吃的鳳梨酥及鳳凰酥 ⬇️完整說明網址⬇️ 👉https://shope.ee/6AOZva951Q #Pp貓 #小潘鳳梨酥 #小潘鳳凰酥 #小潘蛋糕坊 #鳳梨酥 #鳳凰酥 #免運 #鳳梨酥推薦 #鳳凰酥推薦 #