ManageableComponentOptions

ManageableComponentOptions is a plain javascript object with keys of configuration options can be used to config the behaviour & functionality of the ComponentManager that created to manage your React Component.

You can supply ManageableComponentOptions when you create an instance of ComponentManager via new operator directly (for React Class Component) or useComponentManager Hook (for React Function Component).

Available options are:

Option saga

This is an optional config option.

Conceptually, a Saga is like a separate thread created for your component to run an event loop or managing side-effects. Through this option, you can supply a Generator Function (or we call Component Main Saga) to run as a concurrent process. Although, we still physically run in a signle thread environment, the saga model allows us to block a saga when wait for a event / action without blocking other javascript code execution or fork a new saga to create side effects concurrently. We internally use redux-saga to run your saga.

Your saga will be supplied one parameter effects which contains a list of effect creator functions. Those effect creator functions return an object effect description of what needs to be done rather than create the effect immediately. The effect description object will be sent to underlayer saga processor when you yield the effect description object. The underlayer saga processor will then create the effect, monitor its process and resume your saga (the Generator Function) with possible result (through the return value of yield operator) once it's completed.

The effect creator functions provided by effects parameter provide the same functionality as redux-saga. The only difference is that any action related effect creators are namespaced. i.e. you only have access to actions that are dispatched to your component Full Namespace Path and the actions dispatched (i.e. using put effect) are also namespaced. Here is a list of key effect creators:

  • take(pattern)
    • Take an action action with certain pattern.
  • put(action: Action, relativeDispatchPath?: string)
    • Dispatch an action. The action is, by default (when omit the relativeDispatchPath parameter), considered as dispatched from the component Full Namespace Path (i.e. the component itself can receive it). You can supply an optional relativeDispatchPath parameter to alter the default dispatch position on the Namespace Tree.
  • select(selector?: (state: any, ...args: any[]) => any, ...args: any[])
    • Get current state in store and return it or return the result an optional selector function involved on it.
  • takeEvery(pattern, childSaga, ...args: any[])
  • takeLatest(pattern, childSaga, ...args: any[])
  • takeLeading(pattern, childSaga, ...args: any[])
  • throttle(mileseconds, pattern, childSaga, ...args: any[])
  • debounce(mileseconds, pattern, childSaga, ...args: any[])
  • actionChannel(pattern, buffer)

The effects only offers namespaced version effect creator functions that are deal with actions or state as only action dispatch & store operations will be impacted by the namespace. Other effect creator functions (e.g. fork, call etc.) can be accessed directly through redux-saga APIs.

You can also access action/store related effect creator directly from redux-saga APIs. However, If you do so, you will be exposed to 'global scope'. e.g. receive all actions from any namespaces regardless action dispatch process. It's not recommended doing so in a component as it may hurt the component logic encapsulation. You should only access non-action/store related effect creators directly through redux-saga APIs. Any action/store related effect creators should be accessed through the 'effects' parameter of your saga (which are namespaced version).

Here is a sample saga:

import { cancelled } from "redux-saga/effects"
function*(effects) {
    try {
        /**
         * Safe to perform any side effects, monitor any actions 
         * or setup subscription here.
         * You can also fork a new Saga to handle side effects / error concurrently
         * to avoid letting main saga exit.
        */

        // --- while(true) won't freeze your UI as your saga will be blocked
        // --- until the effect is resolved 
        // --- (i.e. an `REQUEST_NEW_GIF` action is received). 
        while(true){
            const action = yield effects.take(actionTypes.REQUEST_NEW_GIF);
            yield effects.put(actions.loadingStart(), "../../../*");
        }
    } catch (e) {
        // --- optional handle any errors here
    } finally {
        /**
         * When your component is unmonted, your saga will be cancelled.
         * Code will reach the finally block and you can test 
         * whether the saga is cancelled by `yield cancelled()` effects
        */
        if (yield cancelled()) {
            // --- logic that should execute only on cancellation
        } else {
            // --- logic that should execute in all situations
        }
    }
};

Option initState

Optional;

Used as intial state of the component. You can also set component initial state in react class component constructor via this.state={...} in which case you don't need this. For react function component, it's the only way to set the initial component state.

Option reducer

Optional;

A reducer for updating component state. This reducer will only see its component state in redux store and only be called when an action is dispatched to the component namespace. You will always want to supply this option when you need component state as the reducer is the only way to alter component state.

Option namespace

String;

e.g. io.github.t83714/Counter It's recommended to use a domain name, a / seperator and a menaingful Component name to create the namespace.

Option namespacePrefix

Optional; String;

If you are the component author, you pronbably never need to set this option. It will be used by the componnent user to pass extra namepsace path to attach your component to a designated position on the namespace tree. It will impact how multicast actions are dispatched & received.

All managed components will accept a namespacePrefix component props. The value of this props will overwrite the namespacePrefix option here during the ComponentManager creation. This allows your component users to customise / config your component action dispatch behaviour.

Option componentId

Optional; String;

You normally don't need to set this. And system will be auto create a componentId for every component. namespacePrefix, namespace plus componentId are made up of a component's full namespace path in a namespace tree.

Option forceOverwriteInitialState

Optional; Boolean;

Default: false; whether overwrite the existing state if it's not empty during the ComponentManager creation.

Option cleanStateDuringDestroy

Optional; Boolean;

Default: true; whether clean up / delete the component state from global store if it's not empty when the ComponentManager is destroyed.

Option actionTypes

Optional; Array of symbol;

Provide all action types defined by your component;

Actions with symbol action type are not serializable. Supply this option here allows ComponentManager to register all your action symbol types. Consequently, make them serializable through

Option allowedIncomingMulticastActionTypes

Optional;

Specify which actionTypes are allowed to be dispatched to this component. By Default, the component will not accept any incoming multicast actions. (Direct address actions will still be delivered). when allowedIncomingMulticastActionTypes is string only "*" is accepted (means accepting any actionTypes).

Option sharedStates

Optional; Object;

You can specify all SharedState that you want to be mapped into your component.

Option namespaceInitCallback

Optional; Function;

namespaceInitCallback & namespaceDestroyCallback will be called once (among all component instances of the same namespace). It's used for required one-off initlalisation job for all same type component (of the same namespace). e.g. create JSS style sheet for the component. namespaceInitCallback is called when Component namespace has just been created. i.e. at least one Component is created & mounted.

Option namespaceDestroyCallback

Optional; Function;

Called when component namespace is destroyed. All components of the namespace are unmounted / destroyed.

Example for namespaceInitCallback & namespaceInitCallback

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { ... };
        this.styleSheet = null;
        this.componentManager = new ComponentManager(this, {
            namespace: "io.github.t83714/Counter",
            reducer: function(state, action) { ... },
            actionTypes,
            allowedIncomingMulticastActionTypes: [actionTypes.INCREASE_COUNT],
            // --- `namespaceInitCallback` is guaranteed only to be called once
            namespaceInitCallback: componentManager => {
                const styleSheet = jss
                    .createStyleSheet(styles, {
                        generateClassName: componentManager.createClassNameGenerator()
                    })
                    .attach();
                return { styleSheet };
            },
            namespaceDestroyCallback: ({ styleSheet }) => {
                styleSheet.detach();
            }
        });
    }

    render() {
        const { styleSheet } = this.componentManager.getNamespaceData();
        const { classes } = styleSheet;
        return (
            <div className={classes.table}>
                <div className={classes.cell}>Counter</div>
                <div
                    className={`${classes.cell} ${
                        classes["counter-container"]
                    }`}
                >
                    <span>{this.state.count}</span>
                </div>
            </div>
        );
    }
}

results matching ""

    No results matching ""