【NextJS】Redux

1. 概要

前回はuseEffectを使いコンポネントのレンダリングする内容についてでした。今回はReduxを使いコンポネント間で値を共有する内容となります。

対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。

2. nodeのインストール

こちらを参考

3. プロジェクトを作成

こちらを参考

4. 必要なライブラリをインストール

こちらを参考

npm install @reduxjs/toolkit react-redux

5. ソースコード

※前回より差分のみを記載

5-1-1. src/app/redux/redux01/page.module.scss

.component {
  color: blue;
  & ul {
    margin-left: 20px;
    & li {
      list-style: disc;
    }
  }
}

5-1-2. src/app/redux/redux01/grandchild.tsx

import Box from "@mui/material/Box";
import { useCounterSelector } from "./hooks";

const Grandchild = () => {
  const count = useCounterSelector((state) => state.counter.count);

  return (
    <Box
      sx={{
        width: "100%",
        p: 2,
        border: "1px dashed grey",
        borderRadius: "20px",
        backgroundColor: "cyan",
        "&:hover": {
          backgroundColor: "white",
          opacity: [0.9, 0.8, 0.7],
        },
      }}
    >
      Grandchild({count})
    </Box>
  );
};

export default Grandchild;

5-1-3. src/app/redux/redux01/child.tsx

import Box from "@mui/material/Box";
import { useCounterSelector } from "./hooks";
import Grandchild from "./grandchild";

const Child = () => {
  const count = useCounterSelector((state) => state.counter.count);

  return (
    <Box
      sx={{
        width: "100%",
        p: 2,
        border: "1px dashed grey",
        borderRadius: "20px",
        backgroundColor: "yellow",
        "&:hover": {
          backgroundColor: "white",
          opacity: [0.9, 0.8, 0.7],
        },
      }}
    >
      Child({count})
      <Grandchild />
    </Box>
  );
};

export default Child;

5-1-4. src/app/redux/redux01/myself.tsx

import { Button, Stack } from "@mui/material";
import Box from "@mui/material/Box";
import scss from "./page.module.scss";
import GoBack from "@/lib/components/go-back";
import { useCounterSelector, useCounterDispatch } from "./hooks";
import { incrementByAmount, Action } from "./counter-slice";
import { AppDispatch } from "./store";
import Child from "./child";

const Myself = () => {
  const count = useCounterSelector((state) => state.counter.count);
  const dispatch: AppDispatch = useCounterDispatch();
  const countUpDown = (action: Action) => {
    dispatch(incrementByAmount(action));
  };

  return (
    <div className={scss.component}>
      <GoBack />
      <br />
      <br />
      <ul>
        <li>Updating the screen</li>
        <ul>
          <li>Sharing data between components</li>
        </ul>
      </ul>
      <br />
      <Box
        sx={{
          width: "100%",
          p: 2,
          border: "1px dashed grey",
          borderRadius: "20px",
          backgroundColor: "orange",
          "&:hover": {
            backgroundColor: "white",
            opacity: [0.9, 0.8, 0.7],
          },
        }}
      >
        Myself({count})
        <Child />
      </Box>
      <br />
      <ul>
        <li>Check the sharing data (Click below button)</li>
      </ul>
      <br />
      <Stack spacing={1} sx={{ width: "50%" }}>
        <Button
          variant="contained"
          size="medium"
          color="primary"
          onClick={() => countUpDown({ symbol: "+", step: 3 })}
        >
          + 3 ({count})
        </Button>
        <Button
          variant="contained"
          size="medium"
          color="secondary"
          onClick={() => countUpDown({ symbol: "-" })}
        >
          - 1 ({count})
        </Button>
      </Stack>
    </div>
  );
};

export default Myself;

5-1-5. src/app/redux/redux01/page.tsx

"use client";

import { Provider } from "react-redux";
import store from "./store";
import Myself from "./myself";

const Redux01 = () => {
  return (
    <Provider store={store}>
      <Myself />
    </Provider>
  );
};

export default Redux01;

5-1-6. src/app/redux/redux01/counter-slice.ts

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import type { RootState } from "./store";

export type Action = {
  symbol: string;
  step?: number;
};

type CounterState = {
  count: number;
};

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

export const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    incrementByAmount: (state, action: PayloadAction<Action>) => {
      const { symbol, step = 1 } = action.payload;
      switch (symbol) {
        case "+":
          state.count += step;
          break;
        case "-":
          state.count -= step;
          break;
        default:
          state.count;
          break;
      }
    },
  },
});

export const { incrementByAmount } = counterSlice.actions;
export const selectCount = (state: RootState) => state.counter.count;
export default counterSlice.reducer;

5-1-7. src/app/redux/redux01/hooks.ts

import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";

export const useCounterDispatch: () => AppDispatch = useDispatch;
export const useCounterSelector: TypedUseSelectorHook<RootState> = useSelector;

5-1-8. src/app/redux/redux01/store.ts

import { combineReducers, configureStore } from "@reduxjs/toolkit";
import counterSlice from "./counter-slice";

const reducers = combineReducers({
  counter: counterSlice,
});

const store = configureStore({
  reducer: reducers,
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

export default store;

5-1-9. src/app/redux/page.module.scss

.components {
  color: blue;
  & ul {
    margin-left: 20px;
    & li {
      list-style: disc;
    }
  }
}

5-1-10. src/app/redux/page.tsx

"use client";

import React from "react";
import { Link } from "@mui/material";

import scss from "./page.module.scss";

const Redux = () => {
  return (
    <div className={scss.components}>
      <ul>
        <li>
          <Link href="/redux/redux01" underline="hover">
            Redux01
          </Link>
        </li>
      </ul>
    </div>
  );
};

export default Redux;

5-1-11. src/lib/common/sidebar-links.tsx

import HomeIcon from "@mui/icons-material/Home";
import AdjustIcon from "@mui/icons-material/Adjust";

import { SidebarLinkType } from "./definitions";

const sidebarHome: SidebarLinkType = {
  label: "Home",
  path: "/",
  icon: <HomeIcon />,
  targetSegment: null,
};
const sidebarComponents: SidebarLinkType = {
  label: "Components",
  path: "/components",
  icon: <AdjustIcon />,
  targetSegment: "components",
};
const sidebarEvents: SidebarLinkType = {
  label: "Events",
  path: "/events",
  icon: <AdjustIcon />,
  targetSegment: "events",
};
const sidebarHooks: SidebarLinkType = {
  label: "Hooks",
  path: "/hooks",
  icon: <AdjustIcon />,
  targetSegment: "hooks",
};
const sidebarRedux: SidebarLinkType = {
  label: "Redux",
  path: "/redux",
  icon: <AdjustIcon />,
  targetSegment: "redux",
};
export const sidebarLinks: SidebarLinkType[] = [
  sidebarHome,
  sidebarComponents,
  sidebarEvents,
  sidebarHooks,
  sidebarRedux,
];

6. サーバーを起動

npm run dev

7. ブラウザで確認

  • http://localhost:3000

8. ディレクトリの構造

.
├── README.md
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│   ├── next.svg
│   └── vercel.svg
├── src
│   ├── app
│   │   ├── components
│   │   │   ├── component01
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── component02
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── component03
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── component04
│   │   │   │   ├── checkbox-demo.tsx
│   │   │   │   ├── page.module.scss
│   │   │   │   ├── page.tsx
│   │   │   │   ├── radio-demo.tsx
│   │   │   │   └── select-demo.tsx
│   │   │   ├── page.module.scss
│   │   │   └── page.tsx
│   │   ├── events
│   │   │   ├── event01
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── page.module.scss
│   │   │   └── page.tsx
│   │   ├── favicon.ico
│   │   ├── globals.css
│   │   ├── globals.scss
│   │   ├── hooks
│   │   │   ├── hook01
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── hook02
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── hook03
│   │   │   │   ├── child.tsx
│   │   │   │   ├── counter-provider.tsx
│   │   │   │   ├── grandchild.tsx
│   │   │   │   ├── myself.tsx
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── hook04
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── hook05
│   │   │   │   ├── child.tsx
│   │   │   │   ├── counter-provider.tsx
│   │   │   │   ├── grandchild.tsx
│   │   │   │   ├── myself.tsx
│   │   │   │   ├── page.module.scss
│   │   │   │   └── page.tsx
│   │   │   ├── hook06
│   │   │   │   ├── child.tsx
│   │   │   │   ├── page.module.scss
│   │   │   │   ├── page.tsx
│   │   │   │   ├── play-provider.tsx
│   │   │   │   ├── text-box.tsx
│   │   │   │   └── video-player.tsx
│   │   │   ├── page.module.scss
│   │   │   └── page.tsx
│   │   ├── layout.module.scss
│   │   ├── layout.tsx
│   │   ├── page.module.scss
│   │   ├── page.tsx
│   │   └── redux
│   │       ├── page.module.scss
│   │       ├── page.tsx
│   │       └── redux01
│   │           ├── child.tsx
│   │           ├── counter-slice.ts
│   │           ├── grandchild.tsx
│   │           ├── hooks.ts
│   │           ├── myself.tsx
│   │           ├── page.module.scss
│   │           ├── page.tsx
│   │           └── store.ts
│   ├── lib
│   │   ├── common
│   │   │   ├── definitions.ts
│   │   │   └── sidebar-links.tsx
│   │   ├── components
│   │   │   ├── alert-snackbar.tsx
│   │   │   ├── go-back.tsx
│   │   │   └── spacer.tsx
│   │   ├── footer.tsx
│   │   ├── header.tsx
│   │   ├── sidebar.tsx
│   │   └── utils
│   │       └── util.ts
│   └── scss
│       └── common
│           ├── _index.scss
│           ├── _mixin.scss
│           ├── _mq.scss
│           └── _variables.scss
├── tailwind.config.ts
└── tsconfig.json

25 directories, 83 files

9. 備考

今回はReduxを使いコンポネント間で値を共有する内容でした。

10. 参考

投稿者プロフィール

Sondon
開発好きなシステムエンジニアです。
卓球にハマってます。

関連記事

  1. 【NextJS】NextJS・TypeScript・Apollo Cl…

  2. 【NextJS】Metadata(Head,Title)・Script…

  3. 【NextJS】ChatApp with Realtime updat…

  4. 【NextJS】Canvasを使い、図形を描画

  5. 【NextJS】Cognito with Amplify(Gen2)+…

  6. 【NextJS】FullCalendar

最近の記事

制作実績一覧

  1. Checkeys