Skip to content

feat: content/1.vue/3.reactivity-2 #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions content/1.vue/3.reactivity-2/.template/files/app.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
<script setup>
const count = ref(1)
const doubled = computed(() => count.value * 2)
<script setup lang="ts">
let todoId = 1

const todoData = reactive({
loading: false,
data: null,
})

function increment() {
count.value++
todoId++
}

async function fetchTodo() {
todoData.loading = true
try {
const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`)
todoData.data = await res.json()
}
finally {
todoData.loading = false
}
}

watch(todoId, fetchTodo, { immediate: true })
</script>

<template>
<div>
<p>count is {{ count }}</p>
<p>doubled is {{ doubled }}</p>
<button @click="increment">
+1
<p>ID: {{ todoId }}</p>
<button type="button" :disabled="todoData.loading" @click="increment">
次の TODO アイテムを取得
</button>
<p v-if="todoData.loading">
Loading...
</p>
<pre v-else>{{ todoData.data }}</pre>
</div>
</template>
42 changes: 28 additions & 14 deletions content/1.vue/3.reactivity-2/.template/solutions/app.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
<script setup>
const count = ref(1)
const multiplier = ref(2)
const result = computed(() => count.value * multiplier.value)
<script setup lang="ts">
const todoData = reactive({
id: 1,
loading: false,
data: null,
})

function increment() {
count.value++
todoData.id++
}
function multiply() {
multiplier.value++

async function fetchTodo() {
todoData.loading = true
try {
const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoData.id}`)
todoData.data = await res.json()
}
finally {
todoData.loading = false
}
}

watch(() => todoData.id, fetchTodo, { immediate: true })

fetchTodo()
</script>

<template>
<div>
<p>count is {{ count }}</p>
<p>result is {{ result }}</p>
<button @click="increment">
+1
</button>
<button @click="multiply">
x{{ multiplier }}
<p>ID: {{ todoData.id }}</p>
<button type="button" :disabled="todoData.loading" @click="increment">
Next Todo
</button>
<p v-if="todoData.loading">
Loading...
</p>
<pre v-else>{{ todoData.data }}</pre>
</div>
</template>
40 changes: 38 additions & 2 deletions content/1.vue/3.reactivity-2/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ ogImage: true

# Reactivity Part 2

In the previous section, we learned about `ref` and `computed`. In this section, we will learn about `reactive` and `watch` that fullfills our basic needs for reactivity.
前章で `ref` `computed` を使った基本的なデータバインディングを学びました。本章では、`reactive` `watch` について学びましょう。この章で基本的なリアクティビティシステムをマスターできます!

// TODO:
- [`reactive()`](https://ja.vuejs.org/api/reactivity-core#reactive) はオブジェクト全体をリアクティブなデータとして扱うことができます。複数のプロパティを持つオブジェクトを管理できます。

- [`watch()`](https://ja.vuejs.org/api/reactivity-core#watch) はリアクティブなデータの変化を監視し、変化があったときに特定の処理を実行することができます。

`reactive` で宣言された値は、JavaScript の[プロキシ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)として返ってくるため、以下のように通常のオブジェクトと同じように操作することができます。

```ts
const counter = reactive({ count: 0 })
counter.count++ // .value は不要
console.log(counter.count) // -> 1
```

::note
**注意**: よくある間違いとして、`ref` はプリミティブな値のみに対応し、オブジェクトには `reactive` を使うという誤解があります。実際は `ref` でも `ref({ count: 0 })` のようにオブジェクトをリアクティブな値として宣言することができます。
また、`reactive` には[いくつかの制限事項がある](https://ja.vuejs.org/guide/essentials/reactivity-fundamentals#limitations-of-reactive)ため、リアクティブな状態を宣言する際は基本的に `ref` を用いるのが推奨されます。
::

`watch` は `computed` と同様にリアクティブ値の変化に応じて作用しますが、主に `console.log` や `fetch` のような副作用をリアクティブに実行するときに使います。
プレイグラウンドでは、サーバーで管理している TODO アイテムを表示していますが、表示する ID が変化したときに新しいアイテムを取得するために `watch` による監視をしています。

`watch` に関する詳しい説明は[ウォッチャーガイド](https://ja.vuejs.org/guide/essentials/watchers)を参照してください。

## チャレンジ問題

今のプレイグラウンドは、TODO アイテムに関するデータを `todoId` と `todoData` の二つのリアクティブ値として管理しています。これらを一つの `todoData` にまとめましょう。
これらのステップを実行することで、`reactive` と `watch` の理解を深めることができるのでぜひ挑戦してみてください!

1. `todoData` に `id` プロパティを追加し、`todoId` を消しましょう。
2. エラー箇所にしたがって、`todoId` と書いてある箇所を `todoData.id` に変えましょう。
- これで `watch` 以外のエラーは解消されるはずです。
3. `watch` の第一引数をゲッター関数に変えましょう。
- `todoData.id` のままだと数値を渡していることになるため、`watch` が変化を補足できません。

もし手詰まりになったら、解決策を確認するためのボタンをクリックして、ヒントを得ることができます。
:ButtonShowSolution{.bg-faded.px4.py2.rounded.border.border-base.hover:bg-active.hover:text-primary.hover:border-primary:50}

ここまでで Vue のリアクティビティシステムの基礎について学びました。次のステップで、Vue の強力な機能の一つである「Composition API」について学びましょう!
Loading