Open Yuya-Furusawa opened 3 years ago
参照
Reduxは必ずしもReactとセットな訳ではない、ただのstoreを管理するためのライブラリ
const store = redux.createStore(reducer);
storeを作るためにはreducerが必要
reducerは現在のstateをactionを引数に新しいstateを返すただの関数
//初期のstate
const initialState = { value: 0 };
//Action Creatorを定義
const increment = () => ({ type: INCREMENT });
const add = (number) => ({ type: ADD, payload: number });
//reducerを定義
const reducer = (state=initialState, action) => {
//typeに応じた処理、switch文とか使う
if (action.type === INCREMENT) {
return { value: state.value + 1 };
}
if (action.type === ADD) {
return { value: state.value + action.payload };
}
return state;
};
actionはユーザーの操作などをトリガーとして外部から与えられる その際、storeにactionを渡すときはdispatchしなくては行けない 多くの場合、Action Creatorを使ってdispatchする
store.dispatch(increment());
以上!基本的にはたったこれだけ! reducerを使うstoreを作り、そのstoreにactionをdispatchするだけ
いよいよReactにReduxのロジックを埋め込んでいく
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<Application />
</Provider>,
document.getElementBtId('root')
);
componentがレンダリングされる・actionがdispatchされるたびに呼び出される
import { useSelector } from 'react-redux';
const CounterComponent = () => {
//引数は関数
const counter = useSelector((state) => state.counter);
return <div>{counter}</div>;
};
useCallback
やReact.memo
で最適化して使うことが多いみたいです
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch();
return (
<div>
<span>{value}</span>
//onClickとかでdispatchとかを使う
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
Hook登場以前の主流なやり方(?)
TODO
参照
selectorはstoreからstateを持ってきてくれる関数
//selectorの例
//単純なselector
const select = state => state.a;
//useSelector
const todos = useSelector(state => state.todos);
これはシンプルな例だが、stateを持ってくる処理が重くなるほどコストが高く、パフォーマンスが低下する
⇒cacheを使おう!Reselectライブラリを使うとそれが簡単にできる
import { createSelector } from 'reselect';
const selectA = state => state.a;
const selectB = state => state.b;
//createSelectorで新しく作られたselector
const selectAB = createSelector(
[selectA, selectB], //selector
(a, b) => { return a+b; }; //selectorを用いた関数
);
const ab = selectAB(state);
createSelectorを使って作られたselectorはcacheが有効
※Reselect以外にもcacheを有効にするライブラリはある e.g) proxy-maximize, re-reselect, reselect-tools, redux-views
参照
普通にreduxのロジックを書くと(特にプロジェクトの規模が大きくなった場合)コードが肥大化し、 可読性が下がる・メンテナンスがしにくくなるなどの弊害が起こる (特にmiddlewareを複数挟むとしんどくなる)
Redux Toolkitを使うと簡潔にReduxのロジックを書くことができる。
①configStore
を使ってstoreを作る
import { configStore } from '@reduxjs/toolkit';
export const store = configStore({ reducer: {} });
②Reactにstoreを渡す
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
③Redux State Sliceを作る Sliceは「initial state, reducer関数, slice name」から、「reducer関数, slice name, action creators, action types」を返す action creators, action typesはreducer関数から自動的に作られる
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter', //sliceの名前
initialState,
reducers: {
increment: (state) => { state.value += 1 },
decrement: (state) => { state.value -= 1 },
incrementByAmount: (state, action) => { state.value += action.payload }
}
});
//action creatorがreducer関数に応じて作成される
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
④Sliceで作ったreducerをstoreに登録する
import counterReducer from '../features/counter/counterSlice'
export const store = configStore({
//sliceごとにReducerを登録
//slice nameを用いる
reducer: { counter: countReducer }
});
⑤Sliceで作ったstateやactionをcomponentで使う
import { useSelector, useDispatch } from 'react-redux';
import { decrement, increment } from './counterSlice'; //これはあくまでもaction creator
export function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
)
};
参照
大規模プロジェクトではReduxを使うことが多いが、それほど規模が大きくない場合、Hooksが登場してからは、useReducer
で済ましてしまうことが多い。
そのときに、Propsのバケツリレーを使うためにuseContext
を併用することが多い。
①Initial Stateを定義する
// countがstate
const initialState = { count: 0 };
②Reducer関数を定義する
const reducerFunc = (state, action) => {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
}
};
③useReducerを使う
// state: stateの参照に使う
// dispatch: stateの更新に使う
const [state, dispatch] = useReducer(reducerFunc, initialState);
④コンポーネントで用いる
const Counter = () => {
const [state, dispatch] = useReducer(reducerFunc, initialState);
return(
<>
<p>Count {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>INCREMENT</button>
<button onClick={() => dispatch({ type: 'decrement' })}>DECREMENT</button>
</>
);
};
参照
Redux
Redux自体はstateを一括管理するライブラリ 必ずしもReactとセットなわけではない
大規模プロジェクトではメリット大(個人開発程度ならオーバーキル) でも最近ではHooksが出てきて
useReducer
+useContext
で賄うことが多い参照