1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[ Rudex ] ReactとReduxでTODOアプリ制作!

Last updated at Posted at 2023-06-21

はじめに

学習のアウトプット残します。間違い等あればご指摘をお願いします。
ReactとRedux-toolkit使用してTODOアプリ作成するだけの記事です。

早速、作っていきます!

<機能>
・テキストエリアの入力したTODOをローカルストレージに保存(初期値にもする)
・TODOの投稿と削除のみ
・Material UI使用 ※使い方について説明していません。

<ディレクトリ構成>
redux_ディレクトリ_image.png

1.必要なパッケージをインストール

Redux Toolkit と React-Redux パッケージをプロジェクトに追加する。
Material UIはこちらからインストール

npm install @reduxjs/toolkit react-redux

2.ストアを作成して初期値(initialState)と状態を更新する関数(reducer)を準備

1.createSlice関数と使ってreducerとactionを生成する
  createSliceにオブジェクトとして設定値を与える。
  ・name(actionタイプ)
  ・initialState(reducerの初期値)
  ・reducers(関数)
  各reducerに渡される引数( state(現在の状態), action(dispatchで送られてくる値) )。

todoSlice.js
import { createSlice } from "@reduxjs/toolkit";

//ローカルストレージから既存のTODOリスト配列を取得
const getTodos = JSON.parse(localStorage.getItem("TAKENOKO_TODO"));

//各タスクのIDを採番する関数
const updateId = (arr) => {
  const newObj = new Array();
  arr.forEach((todo, index) => {
    newObj.push({ id: index, task: todo.task });
  });
  return newObj;
};

const todoSlice = createSlice({
  name: "todo",
  initialState: getTodos || [], //初期値にはローカルストレージのデータか空の配列
  reducers: {                   
    //新たにTODOを追加する
    add: (state, action) => {
      const newTodo = [...state, action.payload];
      localStorage.setItem("TAKENOKO_TODO", JSON.stringify(newTodo));
      return newTodo;
    },
    //TODOを削除する
    remove: (state, action) => {
      const todos = Object.values(state).map((item) => ({ ...item }));
      let updateTodo = todos.filter((todo) => todo.id !== action.payload.id);
      updateTodo = updateId(updateTodo);
      localStorage.setItem("TAKENOKO_TODO", JSON.stringify(updateTodo));
      return updateTodo;
    },
  },
});

export const { add, remove } = todoSlice.actions;
export default todoSlice.reducer;

 
2.storeを生成してreducerをセットする
  configureStoreをインポートしてオブジェクトを引数に与える。
  reducerの設定(複数reducerを設定することができる)。

store.js
import { configureStore } from "@reduxjs/toolkit";
import todosReducer from "../features/todoSlice";

export default configureStore({
  reducer: {
    todos: todosReducer,
  },
});

 
3.グローバルに使用可能にする
  react-reduxからProviderをインポートしてプロジェクト全体を囲む。
  作成したストアをpropsとして渡す。

main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import "@fontsource/roboto/300.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
import { Provider } from "react-redux";
import store from "./app/store";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

 
4.Appコンポーネント作成
  useState等使用しなくてもどのコンポーネントでも状態管理できるようになります🤤

App.jsx
import TodoInput from "./components/todo/TodoInput";
import TodoList from "./components/todo/TodoList";

function App() {
  return (
    <>
      <h1>TODOアプリ</h1>
      <TodoInput />
      <TodoList />
    </>
  );
}

export default App;

 
4.現在のstateの取得と更新方法
  簡単に使い方説明します。※詳しくは公式ドキュメントを見てください。
  ・useSelecterで現在のstateを取得出来ます。
  ・useDispatchにactionを渡してstoreにdispatch出来ます。
   →引数を渡して設定したreducer内で扱えます。

TodoInput.jsx
import { useState } from "react";
import { Button } from "@mui/material";
import { Grid, TextField } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { add } from "../../features/todoSlice";

function TodoInput() {
  const dispatch = useDispatch();
  const todos = useSelector((state) => state.todos);
  const [task, setTask] = useState("");

  //todoを追加するボタン
  const addTodo = () => {
    if (task === "") return;
    const newTodo = { id: todos.length + 1, task: task };
    dispatch(add(newTodo));
    setTask("");
  };

  return (
    <Grid container spacing={5} alignItems="center" justifyContent="center">
      <Grid item>
        <TextField
          value={task}
          type="text"
          margin="dense"
          sx={{
            width: 400,
          }}
          placeholder="タスクを入力しましょう"
          onChange={(e) => setTask(e.target.value)}
        />
      </Grid>
      <Grid item>
        <Button
          onClick={addTodo}
          variant="contained"
          size="large"
          color="secondary"
        >
          追加
        </Button>
      </Grid>
    </Grid>
  );
}

export default TodoInput;

 

TodoList.jsx
import { Box, Button, Stack } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { remove } from "../../features/todoSlice";

function TodoList() {
  const todos = useSelector((state) => state.todos);
  const dispatch = useDispatch();

  //todoを削除するボタン
  const deleteTodo = (e, todo) => {
    dispatch(remove(todo));
  };

  return (
    <Stack
      direction="column"
      justifyContent="center"
      alignItems="center"
      spacing={0.5}
      sx={{
        margin: 2,
      }}
    >
      {todos.map((todo) => {
        return (
          <Box
            key={todo.id}
            sx={{
              display: "flex",
              justifyContent: "space-between",
              alignItems: "center",
              p: 1,
              m: 1,
              background:
                "linear-gradient(167deg, rgba(255, 167, 247, 1), rgba(255, 137, 179, 1) 19%, rgba(224, 216, 239, 1) 31%, rgba(216, 237, 255, 1) 61%)",
              borderRadius: 1,
              width: 500,
              boxShadow: "0px 2px 7px 0px rgba(0, 0, 0, 0.35)",
            }}
          >
            <Box>{todo.task}</Box>
            <Button
              onClick={(e) => deleteTodo(e, todo)}
              sx={{
                color: "red",
                background: "pink",
                "&:hover": {
                  background: "rgba(8, 33, 0, 0.26)",
                },
              }}
            >
              削除
            </Button>
          </Box>
        );
      })}
    </Stack>
  );
}
export default TodoList;

今後、Reduxで非同期処理行う方法についても記事書いてみたいです。
以上になります。

参考にした教材

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?