-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Custom lifecycle-bound scopes (#200)
This PR introduces custom-scoped lifecycle bound graphs. Custom-scoped graphs are a special case of feature-scoped graphs. They differences between the two are: * By default, subgraphs are instantiated lazily. Custom-scoped subgraphs are instantiated immediately when a parent graph with the same scope is instantiated. * When instantiated, custom-scoped subgraphs receive the props of the custom-scoped graph that triggered their instantiation. * Custom-scoped subgraphs can only be instantiated from a lifecycle bound graph with the same scope.
- Loading branch information
Showing
5 changed files
with
212 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
packages/react-obsidian/test/integration/customScopedLifecycleBoundGraphs.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { | ||
Graph, | ||
injectComponent, | ||
LifecycleBound, | ||
ObjectGraph, | ||
Singleton, | ||
} from '../../src'; | ||
import graphRegistry from '../../src/graph/registry/GraphRegistry'; | ||
|
||
describe('custom scoped lifecycle-bound graphs', () => { | ||
it('instantiates custom scoped graphs eagerly', () => { | ||
render(<ComponentTheDoesNotInvokeProviders idx={1} />); | ||
expect(graphRegistry.isInstantiated(CustomScopeGraph)).toBe(true); | ||
}); | ||
|
||
it('instantiates the custom scoped graphs once', () => { | ||
render(<ComponentTheDoesNotInvokeProviders idx={1} />); | ||
render(<ComponentTheDoesNotInvokeProviders idx={2} />); | ||
expect(CustomScopeGraph.idx).toBe(1); | ||
}); | ||
|
||
it('clears the custom scoped subgraph when the main graph is cleared', async () => { | ||
const {unmount} = render(<ComponentTheDoesNotInvokeProviders idx={1} />); | ||
unmount(); | ||
expect(graphRegistry.isInstantiated(CustomScopeGraph)).toBe(false); | ||
}); | ||
|
||
it('clears the custom scoped subgraph only when no other graphs are using it', async () => { | ||
const result1 = render(<ComponentTheDoesNotInvokeProviders idx={1} />); | ||
const result2 = render(<ComponentTheDoesNotInvokeProviders2 />); | ||
|
||
result1.unmount(); | ||
expect(graphRegistry.isInstantiated(CustomScopeGraph)).toBe(true); | ||
result2.unmount(); | ||
expect(graphRegistry.isInstantiated(CustomScopeGraph)).toBe(false); | ||
}); | ||
|
||
it('throws when trying to use a scoped subgraph from an unscoped graph', async () => { | ||
expect(() => { | ||
render(<ComponentThatWronglyReliesOnCustomScopedGraph />); | ||
}).toThrow(/Cannot instantiate the scoped graph 'CustomScopeGraph' as a subgraph of 'UnscopedGraph' because the scopes do not match. undefined !== customScope/); | ||
}); | ||
|
||
it('eagerly instantiates nested scoped graphs', () => { | ||
render(<ComponentThatReliesOnNestedCustomScopedGraph />); | ||
expect(graphRegistry.isInstantiated(CustomScopeGraph)).toBe(true); | ||
}); | ||
}); | ||
|
||
@LifecycleBound({scope: 'customScope'}) @Graph() | ||
class CustomScopeGraph extends ObjectGraph { | ||
public static idx: number; | ||
|
||
constructor(props: Own) { | ||
super(); | ||
CustomScopeGraph.idx = props.idx; | ||
} | ||
} | ||
|
||
@LifecycleBound({scope: 'customScope'}) @Graph({subgraphs: [CustomScopeGraph]}) | ||
class ComponentGraph extends ObjectGraph { | ||
} | ||
|
||
@LifecycleBound({scope: 'customScope'}) @Graph({subgraphs: [CustomScopeGraph]}) | ||
class ComponentGraph2 extends ObjectGraph { | ||
} | ||
|
||
type Own = {idx: number}; | ||
const ComponentTheDoesNotInvokeProviders = injectComponent<Own>( | ||
({idx}: Own) => <>Hello {idx}</>, | ||
ComponentGraph, | ||
); | ||
|
||
const ComponentTheDoesNotInvokeProviders2 = injectComponent( | ||
() => <>Hello</>, | ||
ComponentGraph2, | ||
); | ||
|
||
@Graph({subgraphs: [CustomScopeGraph]}) | ||
class UnscopedGraph extends ObjectGraph { | ||
} | ||
|
||
const ComponentThatWronglyReliesOnCustomScopedGraph = injectComponent( | ||
() => <>This should error</>, | ||
UnscopedGraph, | ||
); | ||
|
||
@Singleton() @Graph({subgraphs: [CustomScopeGraph]}) | ||
class SingletonGraphWithCustomScopeSubgraph extends ObjectGraph { | ||
} | ||
|
||
@LifecycleBound({scope: 'customScope'}) @Graph({subgraphs: [SingletonGraphWithCustomScopeSubgraph]}) | ||
class CustomScopedGraphWithNestedCustomScopeSubgraph extends ObjectGraph { | ||
} | ||
|
||
const ComponentThatReliesOnNestedCustomScopedGraph = injectComponent( | ||
() => <>Hello</>, | ||
CustomScopedGraphWithNestedCustomScopeSubgraph, | ||
); |