まず用語の整理から
State アプリケーションの状態です。
Store Stateの状態を保持している場所
Action ユーザーがアプリケーションで何をしたいかという情報を持つプレーンなオブジェクト
Reducer 発行されたActionを元にStateを更新する副作用の無いメソッド
整理する意味でも図化してみました。
ユーザーの操作によって、コンポーネントから、Actionが生成されます。 後述しますが、コンポーネント上で、この定義ファイルをimportして利用することで、コンポーネントからActionの作成依頼をかけられます。
Actionというディレクトリを切って、そこにActionを発行するメソッドを定義しておきます。 以下は Action Creator と呼ばれるものです。dispatchする時に生の値を渡すのでなく、Action Createrの関数の戻り値を使うことが多いようです。これは、バグを防ぐために有効です。
//Actionはどんなイベントが起こったかを表現するプレーンなオブジェクトです
export const increment = () => {
return {
type: "INCREMENT"
}
}
export const decrement = () => {
return {
type: "DECREMENT"
}
}
actionをdispatchする際は、生の値を渡すのでなく、Action Creatorの戻り値を使う方が安全です。 *以下のように生の値を渡さない。
<button onClick={()=> dispatch({type: "INCREMENT"})}>click</button>
<button onClick={()=> dispatch({type: "DECREMENT"})}>click</button>
Action Creator関数の戻り値を使いましょう。
<button onClick={()=> dispatch(increment())}>click</button>
<button onClick={()=> dispatch(decrement())}>click</button>
Actionは、dispatchしないと、Store内の値を変更することはできません。 コンポーネント内で、Actionをimportして、ActionをDispatchしましょう。
(※Classコンポーネントが主流だった時代は、connect関数が利用されていたようですが、現在は、useSelector と useDispatch でReactとReduxの接続が可能なので、こちらを積極的に使っていった方が良さそうです)
useSelectorを使って、Storeからstate(値)を取得し、useDispatchにAction Creatorを渡して、ActionをDispatchします。
import React from 'react';
import { increment, decrement } from 'Action/actionCreator';
import { useSelector, useDispatch } from 'react-redux';
function App() {
const count = useSelector(state => state.count);
const dispatch = useDispatch()
return(
<>
<div>{count}</div>
<button onClick={()=> dispatch(increment())}>click</button>
<button onClick={()=> dispatch(decrement())}>click</button>
</>
)
}
export default App;
ここでは、Reducerディレクトリを切って、reducer.jsに以下の記述を書いています。 Reducerの関数は二つ引数をとります。第一引数にstate、第二引数にはactionがreturnした値をとります。(typeとpayload)
第一引数のstateですが、基本的には現在の引数の状態を受け取るようになっていますが、もし現在のstateの状態が指定されていない場合は、デフォルトの値をstateの引数に持たせるようにします。
export const initialState = { count: 0 };
export const countReducer = (state = initialState.count, action) => {
switch(action.type) {
case 'INCREMENT':
return {
...state, count: state.count + 1
};
case 'DECREMENT':
return {
...state, count: state.count - 1
}
default: {
return state;
}
}
}
Reducerが作成できたら、Reduxの核となるStoreを初期化して、Propsとして渡しましょう。 Storeは1アプリケーションにつき、一つです。引数には、reducerと初期値を渡します。
const store = createStore(reducer, initialState);
最後にStoreを最上位のコンポーネントでProviderを使って、Storeをpropsとして渡します。これで初めて、子孫コンポーネントの中でReduxの機能が使えるようになります。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { initialState, reducer } from './Reducer/reducer';
const store = createStore(reducer, initialState);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
上述の useSelector と useDispatch によってReactとReduxの接続は実現できています。ユーザーのアクション(ボタンのクリックなど)の応答として、更新された値が表示されます。