Command Scope
If you want to track commands and create an aggregation of the status of commands at a compositional level, the command scope provides a React context for this. This is typically useful when having something like a top level toolbar with a Save button that you want to enable or disable depending on whether or not there are changes within any components used within it.
Using the toolbar scenario as an example; at the top level we can wrap everything in the <CommandScope>
component. This will establish a React context for this part of the hierarchy and track any commands
used within any descendants.
import { CommandScope } from '@cratis/applications/commands';
export const MyComposition = () => {
return (
<CommandScope>
<Toolbar/>
<FirstComponent/>
<SecondComponent/>
</CommandScope>
);
};
The command scope will provide a React context that can be consumed. Within this context there are the following that can be used:
Name | Description |
---|---|
hasChanges | Boolean property that tells whether or not there are changes in any commands within the context |
execute | Method for executing all commands within the context |
revertChanges | Method for reverting any changes to commands within the context |
To consume the command scope context you can use the hook that is provided.
import { useCommandScope } from '@cratis/applications/commands';
export const Toolbar = () => {
const commandScope = useCommandScope();
return (
<div>
<button disabled={!commandScope.hasChanges}>Save</button>
</div>
);
};
The hook is a convenience hook that makes it easier to get the context. You can also consume the context directly by using its consumer:
import { CommandScopeContext } from '@cratis/applications/commands';
export const Toolbar = () => {
return (
<div>
<CommandScopeContext.Consumer>
{value => {
return (
<button disabled={!value.hasChanges}>Save</button>
)
}}
</CommandScopeContext.Consumer>
</div>
);
};
For the <FirstComponent>
we could then have something like below:
export const FirstComponent = () => {
const myCommand = MyCommand.use();
return (
<div>
<input type="text" value={command.someValue} onChange={(e,v) => myCommand.someValue = v; }/>
</div>
)
}
For simplicity <SecondComponent>
exactly the same, just a different command:
export const SecondComponent = () => {
const myCommand = MyOtherCommand.use();
return (
<div>
<input type="text" value={command.someValue} onChange={(e,v) => myCommand.someValue = v; }/>
</div>
)
}
Any changes to properties within these commands will bubble up to the context and the state hasChanges
will be affected
by it.