(Redux)Redux Toolkit、React-Redux の導入

## (Redux)Redux Toolkit、React-Reduxの導入から動作チェックまで

:::note info
version
Next.js:14.2.9
Typescript: 8.5.0
React:18.3.1
reduxjs/toolkit:2.2.7
react-redux:9.1.2
Tailwindcss:3.4.1
:::

## 目的
#### アプリケーションの開発において、状態管理を効率的かつ一元的に行うため


## 参考記事

https://redux-toolkit.js.org/introduction/getting-started

<br>

#### 1.インストール
Redux Toolkit は、最新の Redux のベストプラクティスが詰め込まれており、複雑な設定なしで利用できるため、推奨とされる。
```terminal
npm install @reduxjs/toolkit react-redux
```
<br>

#### 2.Reduxのstoreを作成
`/app` ディレクトリを使う構成で、`store.ts`を作成。

`/app/store/store.ts`
```react
import { configureStore } from '@reduxjs/toolkit';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
// 例: slice のインポート
import counterReducer from './features/counterSlice';

// ストアを作成
export const store = configureStore({
  reducer: {
    // ここに他のスライスを追加
    counter: counterReducer,
  },
});

// 型定義
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;

// カスタムフック
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

```

<br>

#### 3. ReduxのSliceの作成
Sliceは、状態とアクションを定義したモジュール。

`/app/store/features/counterSlice.ts`
```react
import { createSlice } from '@reduxjs/toolkit';

export interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;


```
<br>

#### 4.Providerの設定
`Provider`をアプリ全体に提供するため`_app.tsx`ファイルを設定。
Next.js 14 の`app`ディレクトリを使用する場合、`layout.tsx` に `Provider` を設定。

`/app/layout.tsx`
```react
import './globals.css';
import { Inter } from 'next/font/google';
import { Providers } from './providers';

const inter = Inter({ subsets: ['latin'] });

export const metadata = {
  title: 'Next.js Redux App',
  description: 'A sample Next.js app with Redux',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

```
<br>

`/app/providers.tsx`
```react
'use client';

import { Provider } from 'react-redux';
import { store } from './store/store';

export function Providers({ children }: { children: React.ReactNode }) {
  return <Provider store={store}>{children}</Provider>;
}

```


<br>



#### 5. コンポーネントで Redux を使用
Redux の状態とアクションをコンポーネント内で使用する為に、`useAppSelector` と `useAppDispatch` を使う。
`/app/page.tsx`
```react
'use client';

import { useAppDispatch, useAppSelector } from './store/store';
import { increment, decrement } from './store/features/counterSlice';

export default function Home() {
  const count = useAppSelector((state) => state.counter.value);
  const dispatch = useAppDispatch();

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
      <h1 className="text-4xl font-bold mb-4">Count: {count}</h1>
      <div className="space-x-4">
        <button
          onClick={() => dispatch(increment())}
          className="px-4 py-2 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75 transition"
        >
          Increment
        </button>
        <button
          onClick={() => dispatch(decrement())}
          className="px-4 py-2 bg-red-500 text-white font-semibold rounded-lg shadow-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-opacity-75 transition"
        >
          Decrement
        </button>
      </div>
    </div>
  );
}



```
<br>


## 動作チェック

#### 1.ローカル環境で立ち上げる
ターミナルで下記コマンドを実行し`http://localhost:3000`にアクセス
指定のポート番号がある場合は、その番号にアクセスする
```terminal
npm run dev
```

#### 2.ボタンをクリック

Increment(＋1)もしくはDecrement(-1)をクリックし動作をチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3895618/5c985829-9312-de61-1636-bb2c1f393cd8.png)

<br>
<br>

## まとめ
・`Redux Toolkit` と `React-Redux` をインストール。
・`store`の作成: `store.ts` に `configureStore` を使ってストアを定義。
・`Slice`の作成: `createSlice` を使って状態とアクションを定義。
・`Provider` の設定: `layout.tsx` で `Provider` をラップ。
・コンポーネントで `Redux` を使用: `useAppSelector` と `useAppDispatch` を使って状態とアクションをコンポーネント内で利用。

これで、Next.js 14 で Redux を導入して状態管理を行うことができる。


## 補足
#### Tailwindcssのスタイリング
・`px-4 py-2`: ボタンの左右に `16px`（`4` は `1rem = 16px` に基づく）と上下に `8px` のpaddingを追加。
・`bg-blue-500` と `bg-red-500`: Increment ボタンには青、Decrement ボタンには赤の背景色を指定。
・`text-white`: ボタン内のテキストを白色に指定。
・`font-semibold`: テキストを少し太く（font-weight: 600）することで、読みやすさを向上。
・`rounded-lg`: ボタンの角を丸く（large サイズ）する。
・`shadow-md`: 中程度のシャドウ効果を適用し、ボタンに立体感を追加。
・`hover:bg-blue-700` と `hover:bg-red-700`: マウスオーバー時に、ボタンの背景色をより濃く変更することでインタラクションを強調。
・`focus:outline-none`: フォーカス時にブラウザのデフォルトのアウトラインを消す。
・`focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75`: フォーカス時に外側に青色のリングを表示（`ring` 効果）、透明度は `75%` に設定。
・`transition`: ホバーやフォーカスの状態変化時に滑らかなアニメーション効果を適用。
##### 全体のレイアウト
・`flex flex-col items-center justify-center min-h-screen`: 縦方向に中央揃え、画面の高さいっぱいを使う。
・`space-x-4`: ボタン間の水平方向の余白を `1rem`（16px）追加。

上記スタイリングでIncrement ボタンは青、Decrement ボタンは赤になり、ホバーやフォーカス時に色が変わる。