Skip to content
This repository was archived by the owner on Jan 1, 2025. It is now read-only.

Commit 4a21d43

Browse files
Ryan Holdrenfacebook-github-bot
Ryan Holdren
authored andcommitted
Add flag to skip circular dependency detection (#2214)
Summary: Pull Request resolved: #2214 I discovered a bug in `detectCircularDependencies()` where it may erroneously detect a circular dependency when `graphQLQueryEffect()` is used. It arises from the fact that we are using a global stack to keep track of which Atom/Selectors have been *visited*. From the stack trace, I think I've figured out what is happening... 1) There are a bunch of `selectorGet()` calls to load dependencies. 2) This calls `detectCircularDependencies()` several times and a bunch of keys get added to the `dependencyStack`, as you would expect. So far, everything looks good. 3) Then, because it is a nested dependency, we load an Atom that has a `graphQLQueryEffect()`. 4) The `graphQLQueryEffect()` subscribes to a `RelayObservable`.The act of subscribing schedules a task using the `RelayFBScheduler`. 5) The schedule invokes `flushSyncCallbacks()` in ReactDOM. 6) Which calls `renderWithHooks()` in ReactDOM. 7) Which renders a component that calls `useRecoilValue()`. 8) We then end up back in `detectCircularDependencies()`, which is where we fail because the `dependencyStack` already includes the keys of all those previous Selectors and Atoms up to and including the Atom with the `graphQLQueryEffect()`. This change adds a flag prop named `skipCircularDependencyDetection_DANGEROUS` to `<RecoilRoot>`, which is propagated down to the `Store`. If this flag is set to `true` then `detectCircularDependencies()` is bypassed. This is obviously *less than ideal*. I think we can all agree that a fix for `detectCircularDependencies()` would be a much better solution. I am under the impression that, that will be a major refactor though, since it will require changing `dependencyStack` to be non-global and passing it all around. This flag will at least mitigate the bug until a more proper solution can be implemented. It should be very easy to back it out once that happens. Differential Revision: D44563103 fbshipit-source-id: b396ad068b5dc511664d204e326086cc6b8af29c
1 parent 532afa3 commit 4a21d43

File tree

3 files changed

+9
-0
lines changed

3 files changed

+9
-0
lines changed

packages/recoil/core/Recoil_RecoilRoot.js

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ type InternalProps = {
6565
initializeState?: MutableSnapshot => void,
6666
store_INTERNAL?: Store,
6767
children: React.Node,
68+
skipCircularDependencyDetection_DANGEROUS?: boolean,
6869
};
6970

7071
function notInAContext() {
@@ -365,6 +366,7 @@ function RecoilRoot_INTERNAL({
365366
initializeState,
366367
store_INTERNAL: storeProp, // For use with React "context bridging"
367368
children,
369+
skipCircularDependencyDetection_DANGEROUS,
368370
}: InternalProps): React.Node {
369371
// prettier-ignore
370372
// @fb-only: useEffect(() => {
@@ -486,6 +488,7 @@ function RecoilRoot_INTERNAL({
486488
getGraph,
487489
subscribeToTransactions,
488490
addTransactionMetadata,
491+
skipCircularDependencyDetection_DANGEROUS,
489492
},
490493
);
491494
if (storeProp != null) {
@@ -550,6 +553,7 @@ type Props =
550553
store_INTERNAL?: Store,
551554
override?: true,
552555
children: React.Node,
556+
skipCircularDependencyDetection_DANGEROUS?: boolean,
553557
}
554558
| {
555559
store_INTERNAL?: Store,
@@ -562,6 +566,7 @@ type Props =
562566
*/
563567
override: false,
564568
children: React.Node,
569+
skipCircularDependencyDetection_DANGEROUS?: boolean,
565570
};
566571

567572
function RecoilRoot(props: Props): React.Node {

packages/recoil/core/Recoil_State.js

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export type Store = $ReadOnly<{
132132
getGraph: StateID => Graph,
133133
subscribeToTransactions: ((Store) => void, ?NodeKey) => {release: () => void},
134134
addTransactionMetadata: ({...}) => void,
135+
skipCircularDependencyDetection_DANGEROUS?: boolean,
135136
}>;
136137

137138
export type StoreRef = {

packages/recoil/recoil_values/Recoil_selector.js

+3
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,9 @@ function selector<T>(
11021102
}
11031103

11041104
function selectorGet(store: Store, state: TreeState): Loadable<T> {
1105+
if (store.skipCircularDependencyDetection_DANGEROUS === true) {
1106+
return getSelectorLoadableAndUpdateDeps(store, state);
1107+
}
11051108
return detectCircularDependencies(() =>
11061109
getSelectorLoadableAndUpdateDeps(store, state),
11071110
);

0 commit comments

Comments
 (0)