MeWrite Docs

Zustand store state not updating in component

Zustandストアの状態変更がコンポーネントに反映されない場合の対処法

概要

Zustandストアの状態を更新しても、コンポーネントが再レンダリングされない問題の対処法です。

エラーメッセージ

明示的なエラーは出ませんが、以下の症状が発生します:

  • ストアの状態を更新しても画面が変わらない
  • console.logでストアの値を確認すると更新されているが、UIに反映されない

原因

1. ストア全体を購読している

2. 状態をミュータブルに変更している

3. セレクターの参照が不安定

解決策

1. 必要な状態のみを購読

 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
import { create } from 'zustand'

interface Store {
  count: number
  name: string
  increment: () => void
}

const useStore = create<Store>((set) => ({
  count: 0,
  name: 'John',
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// NG: ストア全体を購読(全状態変更で再レンダリング)
function Component() {
  const store = useStore()
  return <div>{store.count}</div>
}

// OK: 必要な状態のみを購読
function Component() {
  const count = useStore((state) => state.count)
  return <div>{count}</div>
}

2. イミュータブルに状態を更新

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const useStore = create<Store>((set) => ({
  items: [],

  // NG: 直接変更
  addItemBad: (item) => set((state) => {
    state.items.push(item) // ミュータブルな変更
    return state
  }),

  // OK: 新しい配列を作成
  addItemGood: (item) => set((state) => ({
    items: [...state.items, item]
  })),
}))

3. ネストしたオブジェクトの更新

 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
const useStore = create((set) => ({
  user: {
    profile: {
      name: 'John',
      email: 'john@example.com'
    }
  },

  // OK: スプレッド構文でネストを更新
  updateEmail: (email) => set((state) => ({
    user: {
      ...state.user,
      profile: {
        ...state.user.profile,
        email
      }
    }
  })),
}))

// Immerを使う場合
import { immer } from 'zustand/middleware/immer'

const useStore = create(
  immer((set) => ({
    user: { profile: { name: 'John' } },

    updateEmail: (email) => set((state) => {
      state.user.profile.email = email // Immerでは直接変更OK
    }),
  }))
)

4. shallow比較を使用

1
2
3
4
5
6
7
8
9
import { useShallow } from 'zustand/react/shallow'

// 複数の値を購読する場合
function Component() {
  const { count, name } = useStore(
    useShallow((state) => ({ count: state.count, name: state.name }))
  )
  return <div>{count} - {name}</div>
}

5. アクションを分離して購読

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// アクションは再レンダリングを引き起こさない
function Component() {
  const count = useStore((state) => state.count)
  const increment = useStore((state) => state.increment)

  return (
    <button onClick={increment}>
      Count: {count}
    </button>
  )
}

6. デバッグ方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { devtools } from 'zustand/middleware'

const useStore = create(
  devtools(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    { name: 'MyStore' }
  )
)

// Redux DevToolsで状態変更を確認

7. subscribeで変更を監視

1
2
3
4
5
6
7
// コンポーネント外で変更を監視
const unsubscribe = useStore.subscribe(
  (state) => state.count,
  (count, prevCount) => {
    console.log('Count changed:', prevCount, '->', count)
  }
)

関連エラー

関連エラー

React の他のエラー

最終更新: 2025-12-19