長らく現場でmobx-react@4を使用しており、
observerでラップされているコンポーネントではhooksが使えず、
そろそろモヤってきたなぁと感じていました。
長らくmobx周りの知識もUpdateが止まっていたので、
改めて確認してみるとあらまぁ、
mobx-react@6の中にmobx-react-liteが取り込まれているではありませんか!
早速更新だ!と動かしたところ
・
・
・
エラーでアプリが落ちました(´;ω;`)
大まかな原因は
・これまでWarningにしてたcomponetShouldUpdateの実装が、Errorになること
・SPAでのStoreの差し替えが明示的にErrorになること
でした。
(何もかも現コードが悪いです
さて、前者はまぁさほど大変ではないのですが、
後者はどうすれば良いのかと調べました。
mobx-reactが提供するProviderではなく、
いい加減ReactのContextを使えよとのこと。
じゃあ差し替えてやんよ!と差し替えたものの、動かず。
これまでは各種コンポーネントでStoreを参照する際に、
mobx-reactのinjectを使用していたわけですが、
injectパターンもそろそろやめろよとお達しが出ていました。
多分その辺の兼ね合いでダメだったのでしょう。(コードまでは確認していません
思いの外大変な作業だぞーぅと作業を開始したわけですが、ところがどっこい。
実装方法さえ把握できれば意外とシンプルな見た目で、
差し替えのルーティンを黙々と行うだけですみました。
以下が主な実装例です。
(型や一部のexportは省略しているため、おかしなところもあるかも)
context
export const StoresContext = React.createContext(null);
export const StoresProvider: React.FC = ({ children, rootStore }) => (
<StoresContext.Provider
value={{ rootStore }}
>
{children}
</StoresContext.Provider>
);
hooks
import { StoresContext } from 'context';
export const useStores = () => {
const store = React.useContext(StoresContext);
if (!store) {
throw new Error();
}
return store;
};
app
import { StoresProvider } from 'context';
・・・
<StoresProvider rootStore={this.props.store}>
{/* children */}
</StoresProvider>
component
※injectは削除して、useStores hooksを用いてStoreを参照
import { useStores } from 'useStores';
・・・
const Component: React.FC = () => {
const { rootStore } = useStores();
return (...);
};
ちなみに作業中、
可能な限りClassComponentからFunctionalComponentに変えていたのですが、
state管理がえげつないComponentはそのままにしました。
その場合、以下のような形でStoreを参照します。
(詳しくはReact公式のContextの記事を参照してください)
import { StoresContext } from 'context';
class Component extends PureComponent {
render() {
const { rootStore } = this.context;
return (...);
}
}
Component.contextType = StoresContext;