モジュール間で状態を共有
この記事では、Dynamics 365 Commerce のデータ アクションを使用して複数のモジュール間で状態を共有する方法について説明します。
データ アクションは、同じページ上の複数のモジュール間で状態を共有する必要がある場合に、状態管理の重要な役割を果たします。 一般に、実行中の Node アプリケーションのアプリケーション状態内で、状態が共有されます。
例
この例では、2 つのモジュールが基本的な相互作用を共有します。 一方のモジュール (sample-button) にはボタンがあり、もう一方のモジュール (sample-message) にはボタンが選択されている場合にメッセージが表示されます。
最初に、ボタンが選択された回数を含むオブジェクトを返すデータ アクションが必要です。 以下にコードの例を示します。
// sample-state.ts
import { CacheType, createObservableDataAction, IAction, IActionContext, IActionInput, IAny, ICreateActionContext, IGeneric } from '@msdyn365-commerce/core';
export interface ISampleState {
clickCount: number;
}
/**
* SampleState - action input
*/
export class SampleStateInput implements IActionInput {
public getCacheKey = () => `SampleState`;
public getCacheObjectType = () => 'SampleState';
public dataCacheType = (): CacheType => 'request';
}
/**
* SampleState - action
*/
export async function sampleStateAction(input: SampleStateInput, ctx: IActionContext): Promise<ISampleState> {
return { clickCount: 0 };
}
/**
* SampleState - create new input for create action
*/
const createInput = (inputData: ICreateActionContext<IGeneric<IAny>>): IActionInput => {
return new SampleStateInput();
};
/**
* SampleState - create action
*/
export default createObservableDataAction<ISampleState>({
action: <IAction<ISampleState>>sampleStateAction,
input: createInput
});
現在の状態では、このデータ アクションには実装はありません。 キャッシュにオブジェクトを格納する場所を作成するだけです。 この例の 2 つのモジュールは相互に通信する必要があるため、両方でこのオブジェクトを遵守すると便利です。 モジュールにこのオブジェクトへの許可を付与するには、これらのモジュールでは、ページ読み込みデータ アクションとして前に作成したデータ アクションを登録する必要があります。
sample-button モジュールのコードを次に示します。
// sample-button.definition.json
{
"$type": "contentModule",
"friendlyName": "Sample Button",
"name": "sample-button",
"description": "Sample Button",
"categories": ["sample-button"],
"tags": ["samples"],
"module": {
"view": "./sample-button",
"dataActions": {
"sampleState": {
"path": "../../actions/sample-state/sample-state"
}
}
}
}
sample-message モジュールのコードを次に示します。
// sample-message.definition.json
{
"$type": "contentModule",
"friendlyName": "Sample Message",
"name": "sample-message",
"description": "Sample Message",
"categories": ["sample-message"],
"tags": ["samples"],
"dataActions": {
"sampleState": {
"path": "../../actions/sample-state/sample-state"
}
}
}
これで、両方のモジュールがデータ アクションに登録されます。 したがって、これらはどちらもアプリケーション状態で同じオブジェクトを遵守します。 次の手順は、sample-button モジュールにイベントをクリックするユーザーがいる場合、アプリケーション状態を更新することです。 その後、アプリケーション状態を遵守するモジュールはすべて自動的に更新される必要があります。 sample-message モジュールのコードを次に示します。
// sample-message.data.ts
import { AsyncResult } from '@msdyn365-commerce/retail-proxy';
import { ISampleState } from '../../actions/sample-state/sample-state';
export interface ISampleMessageData {
sampleState: AsyncResult<ISampleState>;
}
// sample-message.tsx
import * as React from 'react';
import { ISampleMessageData } from './sample-message.data';
import { ISampleMessageProps } from './sample-message.props.autogenerated';
/**
* SampleMessage Module used for showcasing cross-module communication
* @extends {React.Component<ISampleMessageProps<ISampleMessageData>>}
*/
export default class SampleMessage extends React.Component<ISampleMessageProps<ISampleMessageData>> {
constructor(props: ISampleMessageProps<ISampleMessageData>) {
super(props);
}
public render(): JSX.Element {
if(this.props.data.sampleState.result) {
return (<h3>The Button has been clicked {this.props.data.sampleState.result.clickCount} times.</h3>);
}
return (<h3>Error: No Sample State Detected</h3>);
}
}
sample-message モジュールは非常に簡単です。 ページ読み込みデータ アクションを使用して ISampleState 値を要求しています。 次に、返されるデータに基づいて、単純なメッセージが表示されます。 アプリケーション状態は MobX によって内部で動作するため、このモジュールは変更を遵守しているデータが変化すると自動的に反応できます。
最後に、ユーザー クリック イベントに応じてアプリケーション状態を更新する sample-button モジュールのコードを次に示します。
// sample-button.data.ts
import { AsyncResult } from '@msdyn365-commerce/retail-proxy';
import { ISampleState } from '../../actions/sample-state/sample-state';
export interface ISampleButtonData {
sampleState: AsyncResult<ISampleState>;
}
// sample-button.tsx
import * as React from 'react';
import { ISampleButtonData } from './sample-button.data';
import { ISampleButtonProps } from './sample-button.props.autogenerated';
import { SampleStateInput } from '../../actions/sample-state/sample-state';
/**
* SampleButton component used for showcasing cross-module communication
* @extends {React.Component<ISampleButtonProps<ISampleButtonData>>}
*/
export default class SampleButton extends React.Component<ISampleButtonProps<ISampleButtonData>> {
constructor(props: ISampleButtonProps<ISampleButtonData>) {
super(props);
this._onClick.bind(this);
}
public render(): JSX.Element {
return (
<button onClick={this._onClick}>
Click Me!
</button>
);
}
// OnClick Handler should update application state
private _onClick = (e: React.MouseEvent): void => {
if (this.props.data.sampleState.result) {
// This will directly update our application state, which should trigger all modules observing the state to update
this.props.context.actionContext.update(new SampleStateInput(), { clickCount: this.props.data.sampleState.result.clickCount + 1 });
}
}
}
ご覧のように、onClick ハンドラーが actionContext.update() を呼び出します。 このメソッドを使用すると、アプリケーション状態を直接変更できます。 状態が変更された場合、MobX は sample-message モジュールを含む状態が遵守できるすべてのモジュールを引き継ぎ、再表示します。