「React.js & Next.js超入門」の感想・備忘録3

スポンサーリンク
「React.js & Next.js超入門」の感想・備忘録2の続き

Redux

インストール

npm install redux react-redux

語句

  • State: 値
  • Reducer: Storeに保管されたStateを変更するもの
  • Store: StateとReducerを持っているもの
  • Provider: Storeを他のコンポーネントに渡すもの

使い方

Reducerの作成

  • Reducer=新しいStateを返す関数。
  • action.typeでどのような処理をするかが決まる。
  • Stateが配列の場合、Stateをコピーした配列を操作してからreturnする。
    State配列自体をreturnすると「変更なし」と判断され値が更新されない。
    (同じ配列アドレスがreturnされるため)
const reducer = (state=初期値 , action) => {
  switch (action.type) {
    case 'タイプ1' :
      return 新しいstate;
    case 'タイプ2' :
      return 新しいstate;
    default :
      return state;
  }
};

Storeの作成

const store = createStore(reducer);
createStoredeprecatedとなっているがここではそのまま使う

Providerの記述

import {Provider} from "react-redux";
としてProviderタグで囲む
<Provider store={store}> <Hello/> </Provider>

Storeを使うコンポーネント

connect(関数)(コンポーネント)で必要なStateをpropsに組み込む。
props.dispatch()でReducerを実行する。

カウンタのサンプル

import {createStore} from "redux";
import {Provider} from "react-redux";
import Hello from "./Hello"

const App = () => {
  const reducer = (state=0 , action) => {
    switch (action.type) {
      case 'INCREMENT' :
        return state + 1;
      case 'DECREMENT' :
        return state - 1;
    }
  };
  const store = createStore(reducer);
  return (
    <Provider store={store}>
      <Hello/>
    </Provider>
  );
};

export default App;
import { connect } from 'react-redux';

const Hello = (props) => {
  const handeleButtonClick = () => {
    props.dispatch({type: 'INCREMENT'});
  };
  return (
    <p>{props.count}<button onClick={handeleButtonClick}>dispatch</button></p>
  );
};

const mapStateToProps = state => {
  return {count: state};
}
export default connect(mapStateToProps)(Hello);

メモ帳アプリのサンプル

npx create-react-app memo_app
cd memo_app
npm install redux react-redux
mkdir src/components
touch store.js
touch src/components/{Memo,AddForm,DelForm}.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {Provider} from "react-redux";
import store from "./store"

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App/>
    </Provider>
  </React.StrictMode>
);
import {createStore} from "redux";

const addMemo = (state, action) => {
  // 配列コピー
  const newData = state.slice();
  newData.unshift({message: action.message, created: new Date()});
  return newData;
}
const deleteMemo = (state, action) => {
  const newData = state.slice();
  newData.splice(action.index, 1);
  return newData;
}
const memoReducer = (state= [] , action) => {
  switch (action.type) {
    case 'ADD' :
      return addMemo(state, action);
    case 'DELETE' :
      return deleteMemo(state, action);
    default :
      return state;
  }
};
export const creteAddMemoAction = (message) => {
  return {
    type: 'ADD',
    message: message
  }
};
export const createDeleteMemoAction = (index) => {
  return {
    type: 'DELETE',
    index: index
  }
};
export default createStore(memoReducer);
import Memo from "./components/Memo";
import AddForm from "./components/AddForm";
import DelForm from "./components/DelForm";

const App = () => {
  return (
    <>
      <AddForm/>
      <DelForm/>
      <Memo/>
    </>
  );
};

export default App;
import { connect } from 'react-redux'
import { useState } from "react";
import {creteAddMemoAction} from "../store"

const AddForm = (props) => {
  const [message, setMessage] = useState('');
  const handleButtonClick = () => {
    props.dispatch(creteAddMemoAction(message));
    setMessage('');
  };
  return (
    <>
      <input type="text" value={message} onChange={e=>setMessage(e.target.value)}/>
      <button onClick={handleButtonClick}>addMemo</button>
    </>
  );
};
export default connect()(AddForm);
import { connect } from 'react-redux'

const Memo = (props) => {
  return (
    <ul>
      {props.memos.map((memo, index) => <li key={index}>{memo.message}: {memo.created.toLocaleString()}</li>)}
    </ul>
  );
};
const mapStateToProps = state => {
  return {memos: state};
}
export default connect(mapStateToProps)(Memo);
import { connect } from 'react-redux'
import { useState } from "react";
import {createDeleteMemoAction} from "../store"

const DelForm = (props) => {
  const [index, setIndex] = useState(-1);
  const handleButtonClick = () => {
    if (index === -1) {
      return;
    }
    props.dispatch(createDeleteMemoAction(index));
    setIndex(-1);
  };
  return (
    <>
      <select onChange={e=>setIndex(Number(e.target.value))} value={index}>
        {[<option key="-1" value="-1"></option>, props.memos.map((memo, index)=><option key={index} value={index}>{memo.message}</option>)]}
      </select>
      <button onClick={handleButtonClick}>delMemo</button>
    </>
  );
};
const mapStateToProps = state => {
  return {memos: state};
}
export default connect(mapStateToProps)(DelForm);

コメント