Skip to content

Refactor: Move lastVisitedBoard from raw localStorage to Redux (boardSlice) #160

@ryota-murakami

Description

@ryota-murakami

Summary

BoardPageClientlocalStorage.setItem('gitbox:lastVisitedBoard', ...) で直接 localStorage に書き込み、MaintenanceClientlocalStorage.getItem(...) で直接読み取っている。

このプロジェクトでは既に @laststance/redux-storage-middleware で Redux state を localStorage に自動永続化しているため、この値も boardSlice に統合してデータフローを一本化する。

Current Implementation

Writer (BoardPageClient.tsx L130-138)

useEffect(() => {
  if (typeof window !== 'undefined') {
    localStorage.setItem(
      'gitbox:lastVisitedBoard',
      JSON.stringify({ id: boardId, name: boardName }),
    )
  }
}, [boardId, boardName])

Reader (MaintenanceClient.tsx L194-207)

const [lastVisitedBoard] = useState<{ id: string; name: string } | null>(() => {
  if (typeof window === 'undefined') return null
  const stored = localStorage.getItem('gitbox:lastVisitedBoard')
  if (!stored) return null
  try {
    return JSON.parse(stored) as { id: string; name: string }
  } catch {
    return null
  }
})

Proposed Changes

1. boardSlice.ts — state に lastVisitedBoard を追加

interface BoardState {
  activeBoard: SimplifiedBoard | null
  statusLists: StatusListDomain[]
  repoCards: RepoCardForRedux[]
  lastVisitedBoard: { id: string; name: string } | null  // ← NEW
}

新しい reducer:

setLastVisitedBoard: (state, action: PayloadAction<{ id: string; name: string }>) => {
  state.lastVisitedBoard = action.payload
},

Selector:

export const selectLastVisitedBoard = (state: { board: BoardState }) =>
  state.board.lastVisitedBoard

2. BoardPageClient.tsx — dispatch に置き換え

useEffect(() => {
  dispatch(setLastVisitedBoard({ id: boardId, name: boardName }))
}, [dispatch, boardId, boardName])

typeof window チェックや JSON.stringify が不要になる。

3. MaintenanceClient.tsx — useAppSelector に置き換え

const lastVisitedBoard = useAppSelector(selectLastVisitedBoard)

useState + localStorage.getItem + JSON.parse + try/catch が全て不要になる。

4. テスト更新

  • src/tests/unit/app/board/BoardPageClient.test.tsx — localStorage assertion → Redux state assertion
  • src/tests/unit/app/maintenance/MaintenanceClient.test.tsx — localStorage mock → Redux store mock
  • e2e/logged-in/maintenance-back-to-board.spec.ts — localStorage 直接操作を Redux 永続化キー (gitbox-state) 経由に更新、もしくは board ページ訪問で暗黙的にセットされるフローのままテスト

Benefits

  • Single source of truth: @laststance/redux-storage-middlewareboard slice を既に永続化しているので、追加設定不要
  • Boilerplate 削減: JSON.stringify/parse, typeof window ガード, try/catch が消える
  • 型安全: selector 経由で型が保証される
  • DevTools 可視化: Redux DevTools で値を確認できる

Files to Modify

File Change
src/lib/redux/slices/boardSlice.ts state/reducer/selector 追加
src/app/board/[id]/BoardPageClient.tsx useEffect を dispatch に変更
src/app/maintenance/MaintenanceClient.tsx useState + localStorage → useAppSelector
src/tests/unit/app/board/BoardPageClient.test.tsx テスト更新
src/tests/unit/app/maintenance/MaintenanceClient.test.tsx テスト更新
e2e/logged-in/maintenance-back-to-board.spec.ts E2E テスト更新

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactoringCode quality and structural improvements

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions