Summary
ReactでContext APIを使うときcreateContextを使うファイルとProviderを使うファイルが同じで、Consumerだけ別のファイルで利用した場合、createContextとProviderを同一ファイルで利用すると依存が循環してしまう。
これを気にする場合は、createContxtを使うファイルも分割したほうが良い。
When using Consumer in a separate file when using the Context API in React, it is better not to use createContext and Provider in the same file, because the dependency cycles.
Detail
Providerは子コンポーネントにConsumerを持つことになるので、普通に使うとConsumerへの依存ができてしまう。ProviderとcreateContextで作成したContextが同一ファイルに定義されているとConsumerはContextをimportする必要があるため、Providerの定義されたファイルをimportしてしまい循環する。
これを回避するには、createContextするファイルは独立させ、ProviderとConsumerはそれぞれContextをimportすればよい。
bad
[Context, Provider] -> [Consumer] -> [Context, Provider]
good
[Consumer] -> [Context]
[Provider] -> [Context][Consumer]
型やデフォルト値やContextに渡す値を含めて依存を下図に示す。
サンプルコード内の名前に合わせています。
Sample Code (Good)
// @flow
import { createContext } from 'react';
export type ContextValueType = {
isSpecial: boolean;
}
const defaultValue: ContextValueType = { isSpecial: false };
export const ContextObject = createContext<ContextValueType>(defaultValue);
// @flow
import React from 'react';
import { ContextObject } from './context';
import ContextConsumer from './ContextConsumer';
import type { ContextValueType } from "./context";
const contextValue: ContextValueType = { isSpecial: true };
function App() {
return (
<ContextObject.Provider value={contextValue}>
<ContextConsumer />
</ContextObject.Provider>
);
}
export default App;
// @flow
import { useContext } from 'react';
import { ContextObject } from './context';
export default function ContextConsumer(): string {
const { isSpecial } = useContext(ContextObject);
return isSpecial ? 'special!!!' : 'normal.';
}
Sample Code(bad)
// @flow
import React, { createContext } from 'react';
import ContextConsumer from './ContextConsumer';
export type ContextValueType = {
isSpecial: boolean;
}
const defaultValue: ContextValueType = { isSpecial: false };
export const ContextObject = createContext<ContextValueType>(defaultValue);
const contextValue: ContextValueType = { isSpecial: true };
function App() {
return (
<ContextObject.Provider value={contextValue}>
<ContextConsumer />
</ContextObject.Provider>
);
}
export default App;
// @flow
import { useContext } from 'react';
import { ContextObject } from './App';
export default function ContextConsumer(): string {
const { isSpecial } = useContext(ContextObject);
return isSpecial ? 'special!!!' : 'normal.';
}
ESLint result
$ yarn eslint .
/Users/cw-himura/Program/context-dependecies-sample/src/App.js
3:1 error Dependency cycle detected import/no-cycle
/Users/cw-himura/Program/context-dependecies-sample/src/ContextConsumer.js
3:1 error Dependency cycle detected import/no-cycle
✖ 2 problems (2 errors, 0 warnings)
Top comments (0)