Dojo Store concept Detailed

Translated from: https://github.com/dojo/framework/blob/master/docs/en/stores/supplemental.md

State Objects

In modern browsers, statethe object as CommandRequestpart of the incoming. For stateany changes to the object are converted to the corresponding operation, and then applied to the store.

import { createCommandFactory } from '@dojo/framework/stores/process';
import { State } from './interfaces';
import { remove, replace } from '@dojo/framework/stores/state/operations';

const createCommand = createCommandFactory<State>();

const addUser = createCommand<User>(({ payload, state }) => {
    const currentUsers = state.users.list || [];
    state.users.list = [...currentUsers, payload];
});

Note, IE 11 does not support access to state, if you try to access will immediately throw an error.

StoreProvider

Receiving three properties StoreProvider

  • renderer: A rendering function, which has been injected store, access to sub-state member incoming process.
  • stateKey: Key used for registration status value.
  • paths (Optional): This state is connected to the provider a local.

Fail

StoreProvider There are two ways to trigger the failure and to promote the re-rendering.

  1. The recommended way is by passing the provider pathsto register property path, to ensure that the state will fail when the only relevant changes.
  2. Another is a more general way, when there is no provider is defined as pathwhen, in Store any data changes will cause failure.

Process

The life cycle

Process The implementation of a life cycle, which defines the behavior defined processes.

  1. If the converter is present, it is executed first converter to convert the payload objects
  2. In order to perform synchronization beforemiddleware
  3. Execution command sequence defined
  4. After performing each command (if more than one command is command), an application command operation is returned
  5. If an exception is thrown during the execution of the command, the command will not perform follow-up, and it will not apply the current operation
  6. In order to perform synchronization aftermiddleware

Process Middleware

With optional beforeand aftermethods before and after the process of the middleware. This allows the front and rear behavior defined in general process is added, the operation can be shared.

You can also define multiple middleware in the list. It will be based on a synchronous call middleware order in the list.

Before

beforeMiddleware can get block incoming payloadand storereferences.

middleware/beforeLogger.ts

const beforeOnly: ProcessCallback = () => ({
    before(payload, store) {
        console.log('before only called');
    }
});

After

afterMiddleware block can get passed error(if an error occurs) and the process result.

middleware/afterLogger.ts

const afterOnly: ProcessCallback = () => ({
    after(error, result) {
        console.log('after only called');
    }
});

resultIt implements Proce***esultinterface to provide information about the application to change the store and provide access to the store.

  • executor - allow the process to run on other store
  • store - store references
  • operations - operation of a set of applications
  • undoOperations - a set of operation, operation applied to revoke
  • apply - apply methods on store
  • payload - payload provided
  • id - id of the process for naming

Subscribe to changes in store

StoreThere is a onChange(path, callback)method that a receiver or a set path, and calls the callback function when the state change.

main.ts

const store = new Store<State>();
const { path } = store;

store.onChange(path('auth', 'token'), () => {
    console.log('new login');
});

store.onChange([path('users', 'current'), path('users', 'list')], () => {
    // Make sure the current user is in the user list
});

StoreThere is also an invalidateevent, the event triggered when the store changes.

main.ts

store.on('invalidate', () => {
    // do something when the store's state has been updated.
});

Shared state management

The initial state

When you first create a store, it is empty. Then, a process may be used for the initial filling of the store application state.

main.ts

const store = new Store<State>();
const { path } = store;

const createCommand = createCommandFactory<State>();

const initialStateCommand = createCommand(({ path }) => {
    return [add(path('auth'), { token: undefined }), add(path('users'), { list: [] })];
});

const initialStateProcess = createProcess('initial', [initialStateCommand]);

initialStateProcess(store)({});

Undo

Store using the Dojo patch operation to track changes in the underlying store. In this way, Dojo it is easy to create a set of operation, and then revoked this group operation, in order to recover any data set of command modified. undoOperationsIs Proce***esultpart of, may be afterused in the middleware.

When a process comprising a plurality of modified state store command, and wherein a command fails, rollback, the revocation (Undo) operation is useful.

undo middleware

const undoOnFailure = () => {
    return {
        after: () => (error, result) {
            if (error) {
                result.store.apply(result.undoOperations);
            }
        }
    };
};

const process = createProcess('do-something', [
    command1, command2, command3
], [ undoOnFailure ])

When executed, any command error, the undoOnFailuremiddleware is responsible for the application undoOperations.

It should be noted that undoOperationsonly applies to command the process is completely executed. When rollback state, it will not contain any of the following Operation, change these states may be caused by another process executed asynchronously, or in the state of performing the change in a middleware, or directly on a store operation. These systems cases not undo range.

Optimistic update

Optimistic update can be used to build a responsive UI, although the interaction may take some time to respond, for example, to save a remote resource.

For example, prior to being added if a todo item, may send a request by a persistent object server to update optimism, todo items will be added to the store in order to avoid the embarrassment of waiting or loading indicators. When the server response, success, to coordinate todo item store server according to the result of the operation.

In a successful scenario, use server response provided idto update the added Todoitems, and Todocolor item to green to indicate saved successfully.

In the error scenario, a notification may be displayed, indicating that the request failed, and Todothe color of the item to red while displaying a "Retry" button. Even restore or revoke added Todo items, as well as any other operations that occur in the process.

const handleAddTodoErrorProcess = createProcess('error', [ () => [ add(path('failed'), true) ]; ]);

const addTodoErrorMiddleware = () => {
    return {
        after: () => (error, result) {
            if (error) {
                result.store.apply(result.undoOperations);
                result.executor(handleAddTodoErrorProcess);
            }
        }
    };
};

const addTodoProcess = createProcess('add-todo', [
        addTodoCommand,
        calculateCountsCommand,
        postTodoCommand,
        calculateCountsCommand
    ],
    [ addTodoCallback ]);
  • addTodoCommand - Add a todo item in the application state
  • calculateCountsCommand The number of to-do item to-do recalculate the number of completed items and activities -
  • postTodoCommand- will be presented to the remote service todo items, and use the process of afterthe middleware perform further changes when the error occurred
    • Failure to recover the changes, and failed status field is set to true
    • When successfully use the updated value of the id field todo items returned from the remote service
  • calculateCountsCommand- postTodoCommandafter the successful run once

Synchronization Update

In some cases, before proceeding with the process, the best back-end call is completed. For example, when the process remove an element from the screen, or outlet changed to display different views, triggers the state to restore these operations may make people feel very strange (Annotation: start with the interface data deleted, because deleting the background failure, after a while the data appeared on the screen).

Because the process supports asynchronous command, simply return Promiseto wait for the results.

function byId(id: string) {
    return (item: any) => id === item.id;
}

async function deleteTodoCommand({ get, payload: { id } }: CommandRequest) {
    const { todo, index } = find(get('/todos'), byId(id));
    await fetch(`/todo/${todo.id}`, { method: 'DELETE' });
    return [remove(path('todos', index))];
}

const deleteTodoProcess = createProcess('delete', [deleteTodoCommand, calculateCountsCommand]);

Concurrent command

Process Supports concurrent execution of multiple command, simply put one of these command to the array.

process.ts

createProcess('my-process', [commandLeft, [concurrentCommandOne, concurrentCommandTwo], commandRight]);

In the present example, commandLeftpriority of execution concurrent execution concurrentCommandOneand concurrentCommandTwo. When all concurrent command execution is complete, on-demand application of the results returned. If any concurrent command error will not apply any operation. Finally, the implementation commandRight.

Alternatively the state achieved

When instantiated store, use the default MutableStateimplementation of the interface. In most cases, the default state of the interface have been optimized well enough for common situations. If a particular embodiment requires use another implementation, the incoming can be achieved during initialization.

const store = new Store({ state: myStateImpl });

MutableState API

Any Stateimplementation must provide four ways to correct on the state of the application operation.

  • get&lt;S&gt;(path: Path&lt;M, S&gt;): SA receiving Pathobject and returns the value in the current state of the path points
  • at&lt;S extends Path&lt;M, Array&lt;any&gt;&gt;&gt;(path: S, index: number): Path&lt;M, S['value'][0]&gt;It returns an Pathobject that is positioned to point to the path array index indexvalue
  • path: StatePaths&lt;M&gt;Type-safe way, the state generates a given path Pathobjects
  • apply(operations: PatchOperation&lt;T&gt;[]): PatchOperation&lt;T&gt;[] operation of applications will be provided to the current state

ImmutableState

Dojo Store by Immutable provides an implementation for the MutableState interface. If you make frequent state store, and update deeper level, it is this implementation may improve performance. Before the final decision to use this implementation, you should test and verify performance.

Using Immutable

import State from './interfaces';
import Store from '@dojo/framework/stores/Store';
import Registry from '@dojo/framework/widget-core/Registry';
import ImmutableState from '@dojo/framework/stores/state/ImmutableState';

const registry = new Registry();
const customStore = new ImmutableState<State>();
const store = new Store<State>({ store: customStore });

Local storage

Dojo Store provides a set of tools to use local storage (local storage).

Monitoring changes in the local storage middleware specified path, and using the collectorprovided idand defined in the configuration path, they will be stored on the local disk.

Using local storage middleware:

export const myProcess = createProcess(
    'my-process',
    [command],
    collector('my-process', (path) => {
        return [path('state', 'to', 'save'), path('other', 'state', 'to', 'save')];
    })
);

From LocalStoragethe loadfunction for binding to store

Combined with the state:

import { load } from '@dojo/framework/stores/middleware/localStorage';
import { Store } from '@dojo/framework/stores/Store';

const store = new Store();
load('my-process', store);

Note that the data to be serialized for storage, and the data are overwritten after each call process. This does not apply to not implement the serialized data (such as Dateand ArrayBuffer).

Advanced store operation

Dojo Store using the operation to change the state of the underlying application. Designed operation, will help simplify common interaction of the store, for example, operation will automatically create a support addor replaceinfrastructure required for the operation.

Performing a depth in the uninitialized store add:

import Store from '@dojo/framework/stores/Store';
import { add } from '@dojo/framework/stores/state/operations';

const store = new Store<State>();
const { at, path, apply } = store;
const user = { id: '0', name: 'Paul' };

apply([add(at(path('users', 'list'), 10), user)]);

The results are:

{
    "users": {
        "list": [
            {
                "id": "0",
                "name": "Paul"
            }
        ]
    }
}

Even if the state has not been initialized, Dojo can create a hierarchy based on the underlying path provided. This operation is safe because TypeScript and Dojo provides type safety. This allows the user to use a natural store used by Statethe interface, without the need for explicit saving of interest data store.

When the data requires explicit use, you can testoperate to assert or by obtaining information from the underlying data, and programmatically verified.

This example uses testoperations to ensure initialized to ensure always useradded to the end of the list:

import Store from '@dojo/framework/stores/Store';
import { test } from '@dojo/framework/stores/state/operations';

const store = new Store<State>();
const { at, path, apply } = store;

apply([test(at(path('users', 'list', 'length'), 0))]);

This example programmatically, make sure that useris always added as the last element to the end of the list:

import Store from '@dojo/framework/stores/Store';
import { add, test } from '@dojo/framework/stores/state/operations';

const store = new Store<State>();
const { get, at, path, apply } = store;
const user = { id: '0', name: 'Paul' };
const pos = get(path('users', 'list', 'length')) || 0;
apply([
    add(at(path('users', 'list'), pos), user),
    test(at(path('users', 'list'), pos), user),
    test(path('users', 'list', 'length'), pos + 1)
]);

Prohibit access to state of the root node, if the visit will lead to error, for example, try to execute get(path('/')). This restriction also applies to operation; you can not create a status update root operation. @dojo/framewok/storesThe best practice is to encourage access only store in the minimum necessary parts.

Guess you like

Origin blog.51cto.com/14193089/2437567