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

鱈魚
發佈於Vue
閱讀時間約 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 這類保證資料不變性的套件也是沒問題。


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


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

8會員
10內容數
各種鱈魚滾鍵盤的雜紀
留言0
查看全部
發表第一個留言支持創作者!
鱈魚的魚缸 的其他內容
watch 前先小等一下
閱讀時間約 4 分鐘
Vue reactive 使用筆記
閱讀時間約 1 分鐘
你可能也想看
創作者要怎麼好好休息 + 避免工作過量?《黑貓創作報#4》午安,最近累不累? 這篇不是虛假的關心。而是《黑貓創作報》發行以來可能最重要的一篇。 是的,我們這篇講怎麼補充能量,也就是怎麼休息。
Thumbnail
avatar
黑貓老師
2024-06-29
防曬產品係數測試報告彙整(2024年)從2014年起,自己對於市售防曬產品的效能產生了濃厚的興趣。因為當時候發現不少產品的防曬係數其實標示是有問題的,像是原本應該是人體測試的SPF與PA數值,實際上沒有做,只用機器測試的數據來充當,但這兩者卻有很大的差異。像是防曬係數其實有強度、廣度與平均度三個面向需要一起判斷,但多數廠商並沒有完整標示
Thumbnail
avatar
邱品齊皮膚科醫師
2023-04-27
是誰偷了我的肉 週末兩天連綿的雨,掩飾了小偷的行蹤,周一澆花的時候發現整整齊齊被砍頭的黃金萬年草,看到她禿頭的剎那,一秒入戲,我左右張望了一下,想是哪位路過貪小便宜的路人摘了我家的菜。但是隨即清醒,這是四樓陽台啊! 上個月多肉石頭玉跟玉綴被鳥啄了,蝴蝶蘭的葉子也被切葉蜂切得坑坑巴巴,這
Thumbnail
avatar
懶惰的隱形人
2024-06-04
018閱讀紀錄:人生實用商學院—誰偷了你的錢?(吳淡如)我們明明工作了很久,為什麼覺得自己存不了錢呢?到底錢都花在哪裡呢?我們可以安穩工作到退休嗎?退休能夠不為錢所困擾嗎?以上問題也許都曾是我們心中疑問,但是藉著這本書的六個主題,也許可以提供給我們一個檢視的方向,比較正確,且適合自己的方式,去累積自己的資產,讓自己擁有富足的生活!
Thumbnail
avatar
徐曉梅
2023-05-15
人生實用商學院-誰偷了你的錢?賺了一輩子的錢,卻存不到養老金。 錢怎麼賺,知道;怎麼流失,不知道。 這本書是吳淡如,淡如姐所寫的,他們念的蠻多個商學院,也很努力不斷積極的進修自己,那投資自己很多也想把這些經驗分享出來,所以才有podcast和書的問世。 有fire學姐分享要踩在巨人的肩膀上,那閱讀有經驗成功人士的書籍
Thumbnail
avatar
Ieasy
2023-04-06
好書推薦 | 人生實用商學院 (誰偷了你的錢?)~投資理財新手必讀吳淡如本人因為自己過去不會理財的慘痛經驗 後來去又去念了商學院 把自己所學分享出來 而且是用很簡單的說法讓大家瞬間理解
Thumbnail
avatar
莫非
2022-09-29
誰偷了荷花?青蛙先生一直以來都有失眠的問題,這天,清爽的夜晚,青蛙先生工作到了深夜,眼皮幾乎隨時都要垂下來似的,所以他慢慢地走到家。「嗯?我的床!!」他最心愛的「荷花床」不見了!!!他把一坨泥巴灑在臉上:「清醒點,清醒點!」,他火冒三丈的去找小偷。 他想:「嗯!光靠眼睛不好找!」,所以他拿出「超級放大鏡」,這樣
Thumbnail
avatar
Louis路仁甲
2022-09-21
到底是誰偷了爸爸的內褲?請問有人看到我爸爸的內褲嗎?看到請通知我,感恩!
Thumbnail
avatar
雨兒
2022-08-20
阿姨時代:好久沒偷懶了那今天就先這樣吧!🤪 #阿姨時代:145 / 365
Thumbnail
avatar
吳姓主筆
2022-05-25
吳淡如《人生實用商學院》:誰偷了你的錢?這是一本必須要看兩次而且筆記下來的好書,想想我們用短短一本書的時間就能汲取作者多年的經驗與心法,真的是賺很大啊! (不劇透本書內容,誠摯推薦大家親自去閱讀)
Thumbnail
avatar
射手媽咪婷婷
2022-05-06
《誰偷了班克西?》藝術是生意或公益《誰偷了班克西?》是一部相當雜談且政治意味相當重的紀錄電影,片中以班克西在伯利恆的西岸地區的牆面留下驢子對以色列軍官出示通行證的塗鴉,引起許多政治爭議。而之後某天這面畫著驢子的牆卻被割下來準備出售給競標的藝術收藏家,這讓街頭藝術的公眾性和和過去藝術一樣淪為私人收藏兩個極端,開始了一系列論戰。
Thumbnail
avatar
陸坡 (LUPO)
2020-07-21
《偷書賊》偷走了知識與價值之後與小說《偷書賊》同名之作,乘載著一個承重的歷史,書籍帶來的不只是知識,還包含著難以抹列的記憶與情感,在那大屠殺的年代中,如何建立起對人的仇恨,透過拼裝出來的舉證,讓人無法明瞭真相,何謂真?何謂假?並不是這麼容易分辨的事情,若是無法有足夠的資訊作為判斷依歸,人們依然是很容易被煽動。
Thumbnail
avatar
布雷克
2019-06-19