Redux.Hooks
https://miro.medium.com/max/2380/1*4khgguJQPxUwKSZH3URgQQ.png
Q: mapStateToPropsに変わるものは?
A: useSelector やで
いままではContainer Componentの mapStateToProps でRootStateからPresentational CommponentむけのStateを整形していた。
code:container.tsx
const mapStateToProps = (state: RootState) => ({
app: state.app,
header: headerSelector(state), //reselectとかで整形させてた
});
これを
Presentational Componentに connect して
propsから引っ張り出して
使っていたのが、Container / Presentational で分割する必要がなくなる。
code:presentational.tsx
import { useSelector } from "react-redux";
const selectApp = state => state.app;
const Page: React.FC = props => {
const appState = useSelector(selectApp);
return (
<>
// header state は header 内で select すればええんちゃう
<Header />
<p>isLoading: {app.isLoading}</p>
</>
);
}
Q: なんかいっぱいuseSelectorすることになるが、ええんか?
A: ええんやで
You may call useSelector() multiple times within a single function component. Each call to useSelector() creates an individual subscription to the Redux store. Because of the React update batching behavior used in React Redux v7, a dispatched action that causes multiple useSelector()s in the same component to return new values should only result in a single re-render.
action dispatch で useSelector が起動してStateから引っ張ってくるけど、同一コンポーネント内に複数の useSelector 書いてもそれらのタイミングは1度の re-render で行われる
だから平気っしょ!(と解釈した)
Q: useSelectorはメモ化されとるんか?
A: されない。 ので工夫してね。
When using useSelector with an inline selector as shown above, a new instance of the selector is created whenever the component is rendered.
useSelector 内にその場でセレクタ書くだけならメモ化 されない 。毎回re-renderされる。
useSelector は内部でstateを全く持たないので、メモ化するなら別途工夫が必要
※reselectの createSelector はメモ化機構を内蔵している
Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
メモ化してセレクタが毎回のrenderのタイミングで同じ値を返してしまうのを避けるには、reselect の createSelector などでコンポーネント外部にセレクタを定義すればいい。
コールバックを書くときは re-render を避けるために useCallback するのがパターンだが、そんな感じでパターンで外部に reselect で書く、としてよさそう。
code:todoPage.tsx
import { React } from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
const selectTodos = createSelector(
(todos, app) => {
todos,
isLoading: app.isLoading,
}
);
const TodoPage: React.FC = props => {
return (
<>
// header state は header 内で select すればええんちゃう
<Header />
<p>isLoading: {isLoading}</p>
<ul>
{todos.map((item) => (
<li>{todo.title}</li>
))}
</ul>
</>
);
}
なお、セレクタ内でReact Propsを使いたい際はちょっと考える必要があるみたい。(stateを加工する存在だと思っているのでこのパターンは良くはなさそう、と思うなどした)
Q: mapDispatchToPropsに変わるものは?
A: useDispatch 。こっちは特に考えることなさそう。
コンポーネントにわたしたい時は useCallback で包みましょう。(childで useDispatch する手もありそう)
code:useDispatchEx.tsx
const CounterComponent = props => {
const dispatch = useDispatch()
const incrementCounter = useCallback(
() => dispatch({ type: 'increment-counter' }),
)
return ...
}
(o・▽・o)おわりだよー