Svelte ストアエラー「store is not a function」を解決:writable/derived の落とし穴
Svelte でストアを使った状態管理を実装していると、突然「store is not a function」というエラーに遭遇することがあります。このエラーは、初心者だけでなく経験者でも陥りやすい落とし穴の一つです。
この記事では、writable や derived などのストアを使う際に発生する「store is not a function」エラーの原因と解決方法を徹底解説します。実際のコード例を交えながら、エラーを回避するベストプラクティスもご紹介しますので、ぜひ最後までお読みください。
背景
Svelte ストアの基本的な仕組み
Svelte におけるストアは、コンポーネント間で状態を共有するための強力な仕組みです。ストアを使うことで、親子関係にないコンポーネント同士でも簡単にデータをやり取りできます。
Svelte が提供する主なストア関数には、以下の 3 種類があります。
| # | ストア関数 | 説明 | 用途 |
|---|---|---|---|
| 1 | writable | 読み書き可能なストア | 値の設定・更新が必要な状態管理 |
| 2 | readable | 読み取り専用のストア | 定期的に更新される値やタイマーなど |
| 3 | derived | 他のストアから派生したストア | 計算済みの値や複数ストアの組み合わせ |
以下の図は、Svelte アプリケーション内でストアがどのように機能するかを示しています。
mermaidflowchart TB
comp1["コンポーネントA"] -->|subscribe| store1["writable ストア"]
comp2["コンポーネントB"] -->|subscribe| store1
comp3["コンポーネントC"] -->|subscribe| store1
store1 -->|通知| comp1
store1 -->|通知| comp2
store1 -->|通知| comp3
comp1 -->|set/update| store1
comp2 -->|set/update| store1
図で理解できる要点:
- 複数のコンポーネントが同じストアを購読(subscribe)できる
- ストアの値が変更されると、すべての購読者に自動的に通知される
- writable ストアは任意のコンポーネントから更新可能
writable ストアの基本的な使い方
まずは、writable ストアの基本的な作成方法を見ていきましょう。
javascript// stores.js - ストアファイル
import { writable } from 'svelte/store';
// 初期値を指定してストアを作成
export const count = writable(0);
このコードでは、初期値 0 を持つ writable ストアを作成しています。writable 関数は Svelte の組み込み関数で、読み書き可能なストアオブジェクトを返します。
Svelte コンポーネント内でストアを使う場合、$ プレフィックスを付けることで自動購読できます。
javascript<script>
// ストアをインポート
import { count } from './stores.js';
// ストアの値を更新する関数
function increment() {
count.update(n => n + 1);
}
</script>
<!-- $ プレフィックスで自動購読 -->
<p>カウント: {$count}</p>
<button on:click={increment}>+1</button>
$count という記法により、Svelte が自動的にストアの購読と購読解除を管理してくれます。これにより、メモリリークの心配なく簡潔なコードが書けます。
derived ストアの基本的な使い方
derived ストアは、他のストアから計算された値を提供するストアです。元のストアが更新されると、derived ストアも自動的に再計算されます。
javascript// stores.js
import { writable, derived } from 'svelte/store';
// 元となるストア
export const count = writable(0);
// count ストアから派生したストア
export const doubled = derived(
count,
($count) => $count * 2
);
このコードでは、count ストアの値を 2 倍にした doubled ストアを作成しています。derived 関数の第一引数は元のストア、第二引数は変換関数です。
複数のストアから派生させることもできます。
javascript// stores.js
import { writable, derived } from 'svelte/store';
// 複数の元ストア
export const firstName = writable('太郎');
export const lastName = writable('山田');
// 複数ストアから派生
export const fullName = derived(
[firstName, lastName],
([$firstName, $lastName]) => `${$lastName} ${$firstName}`
);
複数のストアを配列で渡すと、derived 関数は各ストアの値を配列として受け取ります。この例では、姓と名を組み合わせてフルネームを生成しています。
課題
エラーが発生する典型的なパターン
「store is not a function」エラーは、主に以下のような場面で発生します。
エラーコード:TypeError
phpTypeError: store is not a function
at get_store_value (index.mjs:1968:23)
at instance (Component.svelte:42:18)
このエラーメッセージは、Svelte がストアとして扱おうとしたオブジェクトに subscribe メソッドが存在しない場合に表示されます。
以下の図は、エラーが発生する主なパターンを示しています。
mermaidflowchart TB
start["ストア使用開始"] --> check1{"正しいインポート?"}
check1 -->|No| error1["エラー:<br/>store is not a function"]
check1 -->|Yes| check2{"$ 記法を正しく使用?"}
check2 -->|No| error2["エラー:<br/>store is not a function"]
check2 -->|Yes| check3{"derived に<br/>ストアを渡してる?"}
check3 -->|No| error3["エラー:<br/>store is not a function"]
check3 -->|Yes| success["正常動作"]
図で理解できる要点:
- エラーの原因は主に 3 つのチェックポイントで発生
- インポート方法、使用方法、derived の引数が主な確認ポイント
- 順序立てて確認することでエラーを特定しやすい
パターン 1:誤ったインポート方法
最も多いのが、ストアの値を直接インポートしようとするケースです。
javascript<script>
// ❌ 誤り:ストアの値を取得しようとしている import{' '}
{$count} from './stores.js'; console.log($count); //
エラーが発生
</script>
このコードは、stores.js から $count という名前のエクスポートを探そうとしますが、実際にエクスポートされているのは count というストアオブジェクトです。$ 記法はコンポーネント内でのみ有効で、インポート時には使えません。
パターン 2:ストア関数の誤用
writable や derived 関数を誤って二重に呼び出すケースも見られます。
javascript<script>
import {writable} from 'svelte/store'; import {count} from
'./stores.js'; // ❌ 誤り:既にストアであるものを再度
writable で包んでいる const myCount = writable(count);
console.log($myCount); // エラーが発生
</script>
count は既に writable ストアとして作成されているため、それを再度 writable で包む必要はありません。この場合、myCount はストアを含むストアという入れ子構造になり、正しく動作しません。
パターン 3:derived ストアへの値渡し
derived ストアに通常の値を渡してしまうケースも頻発します。
javascript// stores.js
import { writable, derived } from 'svelte/store';
export const count = writable(5);
// ❌ 誤り:ストアの値を直接渡している
export const doubled = derived(5, ($count) => $count * 2);
derived 関数の第一引数には、ストアオブジェクトを渡す必要があります。上記のコードでは、数値 5 を渡しているため、Svelte はそれを subscribe しようとしてエラーが発生します。
パターン 4:コンポーネント外での $ 記法使用
コンポーネント外のファイルで $ 記法を使おうとするケースです。
javascript// utils.js(コンポーネントではない)
import { count } from './stores.js';
// ❌ 誤り:コンポーネント外で $ 記法を使用
export function getDoubledCount() {
return $count * 2; // エラーが発生
}
$ 記法は Svelte コンポーネント(.svelte ファイル)の <script> タグ内でのみ使用できます。通常の JavaScript ファイルでは使えません。
解決策
パターン別の正しい実装方法
それぞれのエラーパターンに対する正しい実装方法を見ていきましょう。
解決策 1:正しいインポート方法
ストアは常にストアオブジェクトとしてインポートし、コンポーネント内で $ 記法を使います。
javascript<script>
// ✅ 正しい:ストアオブジェクトをインポート
import { count } from './stores.js';
// コンポーネント内で $ 記法を使用
console.log($count); // 正常に動作
</script>
<p>カウント: {$count}</p>
このコードでは、count ストアをそのままインポートし、コンポーネント内で $count として値にアクセスしています。これが最も基本的で推奨される方法です。
インポートとエクスポートの正しい対応関係を表にまとめました。
| # | エクスポート側(stores.js) | インポート側(Component.svelte) | 結果 |
|---|---|---|---|
| 1 | export const count = writable(0) | import { count } from './stores.js' | ✅ 正しい |
| 2 | export const count = writable(0) | import { $count } from './stores.js' | ❌ エラー |
| 3 | export const count = writable(0) | import count from './stores.js' | ❌ エラー(default export ではない) |
解決策 2:get 関数を使った値の取得
コンポーネント外でストアの値を取得する必要がある場合は、get 関数を使います。
javascript// utils.js
import { get } from 'svelte/store';
import { count } from './stores.js';
// ✅ 正しい:get 関数を使ってストアの値を取得
export function getDoubledCount() {
const currentCount = get(count);
return currentCount * 2;
}
get 関数は、ストアの現在の値を一度だけ取得します。ただし、get は reactive ではないため、ストアの値が変更されても自動的に再計算されません。
以下は、コンポーネント内とコンポーネント外でのストア利用方法の違いを示した図です。
mermaidflowchart LR
subgraph component ["コンポーネント内(.svelte)"]
dollar["$ 記法<br/>$count"]
end
subgraph outside ["コンポーネント外(.js)"]
getFunc["get 関数<br/>get(count)"]
subscribe["subscribe メソッド<br/>count.subscribe(...)"]
end
store["writable ストア<br/>count"] --> dollar
store --> getFunc
store --> subscribe
dollar -->|自動購読・購読解除| reactive["リアクティブ"]
getFunc -->|一度だけ取得| single["非リアクティブ"]
subscribe -->|手動購読・購読解除| manual["リアクティブ<br/>(手動管理)"]
図で理解できる要点:
- コンポーネント内では $ 記法が最も簡潔で推奨される
- コンポーネント外では get 関数か subscribe メソッドを使用
- リアクティブ性が必要かどうかで使い分ける
解決策 3:subscribe メソッドの活用
ストアの値をリアクティブに監視する必要がある場合は、subscribe メソッドを使います。
javascript// utils.js
import { count } from './stores.js';
// ✅ 正しい:subscribe でリアクティブに監視
export function watchCount(callback) {
// subscribe は購読解除関数を返す
const unsubscribe = count.subscribe((value) => {
callback(value);
});
// クリーンアップ用に購読解除関数を返す
return unsubscribe;
}
このコードでは、subscribe メソッドを使ってストアの変更を監視しています。subscribe は購読解除関数を返すため、それを呼び出すことでメモリリークを防げます。
実際の使用例を見てみましょう。
javascript<script>
import { onDestroy } from 'svelte';
import { watchCount } from './utils.js';
let displayValue = 0;
// ストアの変更を監視
const unsubscribe = watchCount(value => {
displayValue = value * 2;
});
// コンポーネント破棄時に購読解除
onDestroy(() => {
unsubscribe();
});
</script>
<p>2倍の値: {displayValue}</p>
コンポーネントが破棄される際に onDestroy フックで購読を解除することで、メモリリークを防いでいます。
解決策 4:derived ストアの正しい使い方
derived ストアには、必ずストアオブジェクトを渡します。
javascript// stores.js
import { writable, derived } from 'svelte/store';
// 元のストア
export const count = writable(5);
// ✅ 正しい:ストアオブジェクトを渡す
export const doubled = derived(
count,
($count) => $count * 2
);
derived の第一引数に count ストアを渡し、第二引数の関数内で $count として値にアクセスしています。これにより、count が更新されるたびに doubled も自動的に再計算されます。
複数のストアから派生させる場合も同様です。
javascript// stores.js
import { writable, derived } from 'svelte/store';
export const price = writable(1000);
export const quantity = writable(3);
export const taxRate = writable(0.1);
// ✅ 正しい:複数のストアオブジェクトを配列で渡す
export const totalPrice = derived(
[price, quantity, taxRate],
([$price, $quantity, $taxRate]) => {
const subtotal = $price * $quantity;
const tax = subtotal * $taxRate;
return subtotal + tax;
}
);
このコードでは、価格、数量、税率の 3 つのストアから、税込み合計金額を計算しています。いずれかのストアが更新されると、自動的に合計金額も再計算されます。
解決策 5:カスタムストアの実装
より複雑なロジックが必要な場合は、カスタムストアを作成できます。
javascript// stores.js
import { writable } from 'svelte/store';
// カスタムストアを作成する関数
function createCounter() {
// 内部で writable ストアを作成
const { subscribe, set, update } = writable(0);
return {
subscribe, // subscribe メソッドは必須
increment: () => update((n) => n + 1),
decrement: () => update((n) => n - 1),
reset: () => set(0),
};
}
// ✅ 正しい:カスタムストアをエクスポート
export const counter = createCounter();
カスタムストアは、writable ストアをラップして、独自のメソッドを追加したものです。subscribe メソッドを公開することで、Svelte がストアとして認識できます。
コンポーネントでの使用方法は通常のストアと同じです。
javascript<script>
import { counter } from './stores.js';
</script>
<p>カウント: {$counter}</p>
<button on:click={counter.increment}>+1</button>
<button on:click={counter.decrement}>-1</button>
<button on:click={counter.reset}>リセット</button>
カスタムメソッドを使うことで、ストアの操作がより直感的で読みやすくなります。
具体例
実践例 1:ショッピングカート機能
実際のアプリケーションでよくある、ショッピングカート機能を実装してみましょう。
まず、ストアファイルを作成します。
javascript// stores/cart.js
import { writable, derived } from 'svelte/store';
// カート内の商品を管理するストア
function createCart() {
const { subscribe, set, update } = writable([]);
return {
subscribe,
// 商品を追加
addItem: (product) =>
update((items) => {
const existingItem = items.find(
(item) => item.id === product.id
);
if (existingItem) {
// 既存商品の数量を増やす
return items.map((item) =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
// 新規商品を追加
return [...items, { ...product, quantity: 1 }];
}
}),
// 商品を削除
removeItem: (productId) =>
update((items) =>
items.filter((item) => item.id !== productId)
),
// カートをクリア
clear: () => set([]),
};
}
export const cart = createCart();
このカスタムストアは、商品の追加、削除、クリア機能を持っています。既存の商品を追加した場合は数量を増やし、新規の商品は配列に追加します。
次に、derived ストアで合計金額を計算します。
javascript// stores/cart.js(続き)
// カート内の合計金額を計算する derived ストア
export const totalPrice = derived(cart, ($cart) => {
return $cart.reduce((total, item) => {
return total + item.price * item.quantity;
}, 0);
});
// カート内の商品数を計算する derived ストア
export const itemCount = derived(cart, ($cart) => {
return $cart.reduce((count, item) => {
return count + item.quantity;
}, 0);
});
これらの derived ストアは、カートの内容が変更されるたびに自動的に再計算されます。
以下は、ショッピングカートのデータフローを示した図です。
mermaidflowchart TB
user["ユーザー操作"] -->|商品追加| addItem["cart.addItem()"]
user -->|商品削除| removeItem["cart.removeItem()"]
addItem --> cartStore["cart ストア<br/>[商品配列]"]
removeItem --> cartStore
cartStore --> derived1["totalPrice<br/>(derived)"]
cartStore --> derived2["itemCount<br/>(derived)"]
derived1 --> display1["合計金額表示"]
derived2 --> display2["商品数バッジ"]
cartStore --> display3["カート一覧表示"]
図で理解できる要点:
- カスタムストアが商品の追加・削除を処理
- derived ストアが自動的に合計金額と商品数を計算
- コンポーネントは計算済みの値を表示するだけ
コンポーネントでの使用例を見てみましょう。
javascript<script>
// ストアをインポート
import { cart, totalPrice, itemCount } from './stores/cart.js';
// サンプル商品
const sampleProduct = {
id: 1,
name: 'ノートパソコン',
price: 89800
};
function handleAddToCart() {
cart.addItem(sampleProduct);
}
</script>
<div class="cart-summary">
<h2>カート概要</h2>
<p>商品数: {$itemCount}点</p>
<p>合計金額: ¥{$totalPrice.toLocaleString()}</p>
</div>
<button on:click={handleAddToCart}>
カートに追加
</button>
<div class="cart-items">
{#each $cart as item (item.id)}
<div class="item">
<span>{item.name}</span>
<span>¥{item.price.toLocaleString()}</span>
<span>×{item.quantity}</span>
<button on:click={() => cart.removeItem(item.id)}>
削除
</button>
</div>
{/each}
</div>
{#if $itemCount > 0}
<button on:click={() => cart.clear()}>
カートをクリア
</button>
{/if}
このコンポーネントでは、$cart、$totalPrice、$itemCount という 3 つのストアを使っています。すべて $ 記法で自動購読されるため、値が変更されると自動的に再レンダリングされます。
実践例 2:フォームバリデーション
フォームのバリデーション状態を管理するストアを実装してみましょう。
javascript// stores/form.js
import { writable, derived } from 'svelte/store';
// フォームの各フィールドを管理
export const email = writable('');
export const password = writable('');
export const passwordConfirm = writable('');
まず、各フィールド用の writable ストアを作成します。これらのストアには、ユーザーが入力した値が格納されます。
次に、各フィールドのバリデーションを行う derived ストアを作成します。
javascript// stores/form.js(続き)
// メールアドレスのバリデーション
export const emailValid = derived(email, ($email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test($email);
});
// パスワードのバリデーション(8文字以上)
export const passwordValid = derived(
password,
($password) => {
return $password.length >= 8;
}
);
// パスワード確認のバリデーション
export const passwordConfirmValid = derived(
[password, passwordConfirm],
([$password, $passwordConfirm]) => {
return (
$password === $passwordConfirm &&
$passwordConfirm !== ''
);
}
);
各 derived ストアは、対応するフィールドの値をチェックして、バリデーション結果を true/false で返します。
最後に、全体のフォームが有効かどうかを判定する derived ストアを作成します。
javascript// stores/form.js(続き)
// フォーム全体のバリデーション
export const formValid = derived(
[emailValid, passwordValid, passwordConfirmValid],
([
$emailValid,
$passwordValid,
$passwordConfirmValid,
]) => {
return (
$emailValid && $passwordValid && $passwordConfirmValid
);
}
);
この formValid ストアは、すべてのフィールドが有効な場合のみ true を返します。
以下は、フォームバリデーションの構造を示した図です。
mermaidflowchart TB
subgraph inputs ["入力フィールド(writable)"]
email["email"]
password["password"]
passwordConfirm["passwordConfirm"]
end
subgraph validation ["バリデーション(derived)"]
emailValid["emailValid"]
passwordValid["passwordValid"]
passwordConfirmValid["passwordConfirmValid"]
end
subgraph result ["結果(derived)"]
formValid["formValid"]
end
email --> emailValid
password --> passwordValid
password --> passwordConfirmValid
passwordConfirm --> passwordConfirmValid
emailValid --> formValid
passwordValid --> formValid
passwordConfirmValid --> formValid
formValid --> submit["送信ボタン<br/>有効/無効"]
図で理解できる要点:
- 入力値から段階的にバリデーション結果を導出
- 複数のバリデーションを組み合わせて最終結果を生成
- 階層的な derived ストアの構造で複雑なロジックを実現
コンポーネントでの使用例です。
javascript<script>
import {
email,
password,
passwordConfirm,
emailValid,
passwordValid,
passwordConfirmValid,
formValid
} from './stores/form.js';
function handleSubmit() {
if ($formValid) {
console.log('フォーム送信:', {
email: $email,
password: $password
});
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<div class="field">
<label for="email">メールアドレス</label>
<input
id="email"
type="email"
bind:value={$email}
/>
{#if $email && !$emailValid}
<p class="error">有効なメールアドレスを入力してください</p>
{/if}
</div>
<div class="field">
<label for="password">パスワード</label>
<input
id="password"
type="password"
bind:value={$password}
/>
{#if $password && !$passwordValid}
<p class="error">パスワードは8文字以上で入力してください</p>
{/if}
</div>
<div class="field">
<label for="password-confirm">パスワード(確認)</label>
<input
id="password-confirm"
type="password"
bind:value={$passwordConfirm}
/>
{#if $passwordConfirm && !$passwordConfirmValid}
<p class="error">パスワードが一致しません</p>
{/if}
</div>
<button type="submit" disabled={!$formValid}>
登録
</button>
</form>
<style>
.error {
color: red;
font-size: 0.875rem;
margin-top: 0.25rem;
}
</style>
このコンポーネントでは、bind:value を使ってストアと入力フィールドを双方向バインディングしています。ユーザーが入力するたびに、対応する derived ストアが自動的に再計算され、エラーメッセージの表示や送信ボタンの有効/無効が切り替わります。
実践例 3:API データの管理
API から取得したデータをストアで管理する例を見てみましょう。
javascript// stores/users.js
import { writable, derived } from 'svelte/store';
// ユーザーデータを管理するカスタムストア
function createUserStore() {
const { subscribe, set, update } = writable({
data: [],
loading: false,
error: null,
});
return {
subscribe,
// ユーザーデータを取得
fetchUsers: async () => {
// ローディング状態に設定
update((state) => ({
...state,
loading: true,
error: null,
}));
try {
const response = await fetch(
'https://api.example.com/users'
);
if (!response.ok) {
throw new Error(
`HTTP error! status: ${response.status}`
);
}
const data = await response.json();
// データを設定
update((state) => ({
...state,
data: data,
loading: false,
}));
} catch (error) {
// エラーを設定
update((state) => ({
...state,
loading: false,
error: error.message,
}));
}
},
// ストアをリセット
reset: () =>
set({
data: [],
loading: false,
error: null,
}),
};
}
export const users = createUserStore();
このカスタムストアは、API データ、ローディング状態、エラー情報を一つのオブジェクトで管理しています。非同期処理の各段階で適切に状態を更新します。
derived ストアで特定のユーザーをフィルタリングすることもできます。
javascript// stores/users.js(続き)
// アクティブなユーザーのみをフィルタリング
export const activeUsers = derived(users, ($users) => {
return $users.data.filter((user) => user.active === true);
});
// ユーザー数をカウント
export const userCount = derived(users, ($users) => {
return $users.data.length;
});
コンポーネントでの使用例を見てみましょう。
javascript<script>
import { onMount } from 'svelte';
import { users, activeUsers, userCount } from './stores/users.js';
// コンポーネント初期化時にデータを取得
onMount(() => {
users.fetchUsers();
});
</script>
<div class="user-list">
<h2>ユーザー一覧(全{$userCount}件)</h2>
{#if $users.loading}
<p>読み込み中...</p>
{:else if $users.error}
<p class="error">エラー: {$users.error}</p>
<button on:click={() => users.fetchUsers()}>
再試行
</button>
{:else}
<h3>アクティブユーザー</h3>
<ul>
{#each $activeUsers as user (user.id)}
<li>{user.name} - {user.email}</li>
{/each}
</ul>
{/if}
</div>
このコンポーネントでは、ローディング状態、エラー状態、データ表示を適切に切り替えています。derived ストアを使うことで、アクティブなユーザーのみを簡単に表示できます。
まとめ
Svelte の「store is not a function」エラーは、ストアの仕組みを正しく理解することで確実に回避できます。
この記事で解説した主要なポイントを振り返りましょう。
| # | 項目 | 重要ポイント |
|---|---|---|
| 1 | インポート | ストアオブジェクトをインポートし、$ 記法はコンポーネント内でのみ使用 |
| 2 | コンポーネント外 | get 関数または subscribe メソッドを使用 |
| 3 | derived ストア | 必ずストアオブジェクトを渡し、値を渡さない |
| 4 | カスタムストア | subscribe メソッドを必ず公開する |
| 5 | エラー対処 | エラーメッセージから原因を特定し、正しいパターンに修正 |
エラーが発生した際は、まずインポート方法と使用方法を確認しましょう。$ 記法の使用場所、derived に渡している引数、コンポーネント外でのアクセス方法など、チェックポイントは明確です。
Svelte のストアは、適切に使えば非常に強力な状態管理ツールになります。writable、readable、derived の特性を理解し、カスタムストアで独自のロジックを実装することで、保守性の高いアプリケーションを構築できるでしょう。
エラーを恐れず、この記事で紹介したパターンを参考に、Svelte ストアを活用してみてください。
関連リンク
articleSvelte ストアエラー「store is not a function」を解決:writable/derived の落とし穴
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleSvelteKit 本番運用チェックリスト:CSP/SRI/Cache-Control/Headers 総点検
articleSvelte フォーム体験設計:Optimistic UI/エラー復旧/再送戦略の型
articleSvelte を macOS + yarn + TypeScript で最短構築:ESLint/Prettier まで一気通貫
articleSvelte 旧リアクティブ記法 vs Runes:可読性・コード量・パフォーマンス比較
articlePlaywright Debug モード活用:テストが落ちる原因を 5 分で特定する手順
articleVue.js でメモリリーク?watch/effect/イベント登録の落とし穴と検知法
articleTailwind CSS のクラスが消える/縮む原因を特定:ツリーシェイクと safelist 完全対策
articlePHP 構文チートシート:配列・クロージャ・型宣言・match を一枚で把握
articleSvelte ストアエラー「store is not a function」を解決:writable/derived の落とし穴
articleNext.js の 観測可能性入門:OpenTelemetry/Sentry/Vercel Analytics 連携
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来