LoginSignup
0
0

Redux Toolkit Tutorialを通してreduxを学ぶ #3

Last updated at Posted at 2023-05-27

はじめに

本記事は、「Redux Toolkit Tutorialを通してreduxを学ぶ #2」の続きです。

・Reduxを使用した場合
・Reduxを使用しなかった場合

の両方のコードを比較し、最後にreduxを活用するメリットについて感じたことを記述します。

目次

  • #3でやりたいこと
  • Reduxを使用した場合の実装
  • Redux使用有無を比較し感じたメリット

やりたいこと

#2で実装したソースを、Reduxを使用した場合の実装に変更していく

Reduxを使用した場合の実装

以降、#2で作成したサンプルプロジェクトを別名でコピーして、
コピー先プロジェクトに編集を加えていく。

$cp -R sample_ts_project_noRedux sample_ts_project_withRedux 

まず必要なパッケージをインストールする

$ npm install --sav-dev @reduxjs/toolkit react-redux

srcフォルダ配下にstore.tsxを作成する。

$ touch src/store.tsx
src/store.tsx
import { PayloadAction, configureStore, createSlice } from "@reduxjs/toolkit";

interface UserStateValue {
    username: string;
}
interface UserState {
    value: UserStateValue;
}

const initialState = { value: {username: ""} } as UserState;
const userSlice = createSlice({
    name: "user",
    initialState,
    reducers: {
        login: (state: UserState, action: PayloadAction<UserStateValue> ) => {
            state.value = action.payload;
        },

        logout: (state: UserState) =>{
//  state must be immutable so this won't work
            // state = initialState;
// do this way
            state.value = initialState.value;
        },
    },
});

export const { login, logout } = userSlice.actions;
export const store = configureStore({
    reducer: {
        user: userSlice.reducer,
    },
});

App.txsからpropsで全ての子コンポーネントにステートを渡していた部分の記述が不要になる。
その代わりに、Providerタグで要素を囲む。

components/App.tsx
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
import Contact from "./Contact";
- import { useState } from "react";
+ import { Provider } from "react-redux";
+ import { store } from "../store";

function App() {
-  const [newUsername, setNewUsername] = useState<string>("");

-  function updateUsername(name: string){
-    setNewUsername(name);
-  }

  return (
    <div className="App">
+      <Provider store={store}>
        <Router>
          <Link to="/">Home </Link>
          <Link to="/login">Login </Link>
          <Link to="/contact">Contact </Link>
          <Routes>
-            <Route path="/" element={<Home username={newUsername} />} />
+            <Route path="/" element={<Home />} />
-            <Route path="/login" element={<Login username={newUsername} onUpdate={updateUsername}/>} />
+            <Route path="/login" element={<Login />} />
-            <Route path="/contact" element={<Contact username={newUsername} />} />
+            <Route path="/contact" element={<Contact />} />
          </Routes>
        </Router>
+      </Provider>
    </div>
  );
}

export default App;

以下メソッドを用いることで、グローバルなステートを管理できる。
useDispatch:ステートを更新する
useSelector:ステートを取得する

components/Login.tsx
import { useState } from "react";
+ import { login, logout } from "../store";
+ import { useDispatch, useSelector } from "react-redux";
import Header from "./Header";
import Footer from "./Footer";

- interface Props{
-   username: string;
-   onUpdate: (name:string) => void;
- }

- function Loign(props: Props) {
+ function Loign() {
  const [inputUsername, setInputUsername] = useState<string>("");

+  const dispatch = useDispatch();
+  const username = useSelector((state: any) =>  state.user.value.username);
+  console.log("usename: " +username);
-  console.log("usename: " +props.username);


  function handleLogin(event:any) {
    event.preventDefault();
-   props.onUpdate(inputUsername);
+    dispatch(login({username: inputUsername}));
    setInputUsername("");
  }

  function handleLogout(event:any){
    event.preventDefault();
-   props.onUpdate("");
+    dispatch(logout());   
    setInputUsername("");

  }

  return (
    <div>
      <Header />
      <div className="login">
-        {props.username}
+        {username}
        <form className="login">
          <input
            name="loginV"
            placeholder="loginPH"
            value={inputUsername}
            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                console.log(e.target.value);
                setInputUsername(e.target.value);
            }}
          />
          <button onClick={handleLogin}> Submit login</button>
          <button onClick={handleLogout}> Logout</button>
        </form>
      </div>
      <Footer />
    </div>
  );
}

export default Loign;
components/Contact.tsx
import Header from "./Header";
import Footer from "./Footer";
+ import { useSelector } from "react-redux";

- interface Props{
-   username: string;
- }

- function Contact(props: Props) {
+ function Contact() {
+  const username = useSelector((state: any) => state.user.value.username);
  return (
    <div>
      <Header />
-      <div className="contact">Contact {props.username}</div>
+      <div className="contact">Contact {username}</div>
      <Footer />
    </div>
  );
}

export default Contact;
components/Home.tsx
import Header from "./Header";
import Footer from "./Footer";
+ import { useSelector } from "react-redux";

- interface Props{
-   username: string;
- }

- function Home(props:Props) {
+ function Home() {
    const username = useSelector((state: any) => state.user.value.username);
  return (
    <div>
      <Header/>
-      <div className="home"> Home route {props.username}</div>
+      <div className="home"> Home route {username}</div>
      <Footer/>
    </div>
  );
}

export default Home;

Redux使用有無を比較し感じたメリット

  • コンポーネント間のステートの受け渡しが容易になる
    • 親子関係ではない場合など
  • useStateで管理するステートがコンポーネント内で完結する
    - コンポーネント内にステートを追加した時などに他のコンポーネントへの副作用がない
  • コードがシンプルになる
    - 親子関係だが、親と通い孫コンポーネントだけでステートを使いたい場合など
     (バケツリレーみたいにpropsを渡さなくていい)

簡単なサンプルプロジェクトで試してみたが、なんとなく便利なものなんだなということは理解できた。
今後はAPI呼び出しなど、より発展的な使い方も勉強していこうと思う。

関連記事

0
0
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
0
0