長らく現場で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;