With useSelector(), returning a new object every time will always force a re-render by default.
(Equality Comparisons and Updatesより引用
npx create-react-app react-redux-example --template redux
count 取得と表示を別コンポーネントに分ける
const CountDisplay = () => {
const count = useSelector(selectCount);
return <span className={styles.value}>{count}</span>;
};
// 呼び出し側
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
+ lastAction: 'no action',
},
reducers: {
increment: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1;
+ state.lastAction = 'increment';
},
decrement: state => {
state.value -= 1;
+ state.lastAction = 'decrement';
},
incrementByAmount: (state, action) => {
state.value += action.payload;
+ state.lastAction = 'incrementByAmount';
},
},
});
lastAction を表示するコンポーネントを 2 つ用意
export const PrimitiveSelector = () => {
const lastAction = useSelector(state => state.counter.lastAction);
return <div className={styles.primitiveSelector}>{lastAction}</div>;
};
export const ObjectSelector = () => {
const { lastAction } = useSelector(state => state.counter);
return <div className={styles.objectSelector}>{lastAction}</div>;
};
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
selectCount,
} from "./counterSlice";
import styles from "./Counter.module.css";
+import { PrimitiveSelector, ObjectSelector } from "./LastAction";
const CountDisplay = () => {
const count = useSelector(selectCount);
return <span className={styles.value}>{count}</span>;
};
export function Counter() {
const dispatch = useDispatch();
const [incrementAmount, setIncrementAmount] = useState("2");
return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
<CountDisplay />
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
</div>
+ <div className={styles.row}>
+ <PrimitiveSelector />
+ <ObjectSelector />
+ </div>
<div className={styles.row}>
<input
className={styles.textbox}
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button
className={styles.button}
onClick={() =>
dispatch(incrementByAmount(Number(incrementAmount) || 0))
}
>
Add Amount
</button>
<button
className={styles.asyncButton}
onClick={() => dispatch(incrementAsync(Number(incrementAmount) || 0))}
>
Add Async
</button>
</div>
</div>
);
}
yarn start
DevTool で Highlight updates when components render.
にチェックをいれる
さっきのドキュメントの続きに書いてある
- Call useSelector() multiple times, with each call returning a single field value
- Use Reselect or a similar library to create a memoized selector that returns multiple values in one object, but only returns a new object when one of the values has changed.
- Use the shallowEqual function from React-Redux as the equalityFn argument to useSelector(), like:
(Equality Comparisons and Updatesより引用
React に優しい僕でありたい - Qiita のコメント
上の React のパフォーマンス最適化ドキュメントにもあるが、サブコンポーネントにも影響してしまうので野放しにしていいわけでもなさそう