MobX обрабатывает вложенные объекты в нескольких хранилищах - PullRequest
0 голосов
/ 18 апреля 2019

Я работаю над страницей для редактирования задач, назначаемых сотрудникам, которым назначены ставки заработной платы и разрешения.

Данные о состоянии страницы имеют следующую форму:

TaskObject { 
    Model : Object, // where the actual data of the TaskObject is stored
    PermissionLinks : PermissionLink[],
    Payrates : PayRate[],
    OriginalObject : TaskObject
}

На странице может быть так много действий, которые могут происходить в самой задаче, виджете ставок оплаты,и страницу ссылок разрешений, которую я решил разделить хранилища на , как описано здесь , например:

TasksStore

/**
 * @class
 * The state of the entire tasks page
 */
class TasksStore { 
    /**
     * @field {TaskObject[]}
     */
    tasks = [];

    taskIndex = -1;

    // child stores
    payRatesStore;

    permissionLinksStore;

    // this is part of what was done here: https://mobx.js.org/refguide/computed-decorator.html#computeds-with-arguments
    _tasksCache = new Map();
    _taskIndexCache = new Map();

    constructor() { 
        // initialize the stores in the constructor
        this.payRatesStore = new PayRatesStore(this);
        this.permissionLinksStore = new PermissionsStore(this);

        mobx.autorun(() => {
            console.log('this.tasks === ', this.tasks);
        })
    }

    // getters
    get tasksHaveChanged() { 
        return this.tasks.reduce((prevTask, currentTask) => { 
            return ((prevTask.IsChanged()) && (currentTask.IsChanged()))
        })
    }

    /**
     * @return {TaskObject} the current task
     */
    get currentTask() { 
        if ((!this.tasks) || (this.taskIndex === -1)) { 
            return null
        }
        return this.tasks[this.taskIndex]
    }

    // TODO: Should we, and if so, how, to mark this method computed (Mobx cannot mark arg-taking functions as @computed)
    /**
     * Returns the index of task with respect to this list of tasks
     * @param {TaskObject} task 
     */
    indexOf(task) { 
        // Implementation for this is inspired by https://mobx.js.org/refguide/computed-decorator.html#computeds-with-arguments
        const id = task.Model.Id
        if (this._taskIndexCache.has(id)) { 
            return this._taskIndexCache.get(id).get()
        }

        const computedFind = computed(() => this.tasks.find((val) => (JSON.stringify(val) === JSON.stringify(task))))
        this._taskIndexCache.set(id, computedFind)
        return computedFind.get()
    }

    /**
     * Returns the index of task with respect to this list of tasks
     * @param {TaskObject} task 
     * @return {TaskObject} the task, if found, or undefined
     */
    find(task) { 
        // Implementation for this is inspired by https://mobx.js.org/refguide/computed-decorator.html#computeds-with-arguments
        const id = task.Model.Id
        if (this._tasksCache.has(id)) { 
            return this._tasksCache.get(id).get()
        }

        const computedFind = computed(() => this.tasks.find((val) => (JSON.stringify(val) === JSON.stringify(task))))
        this._tasksCache.set(id, computedFind)
        return computedFind.get()
    }

    // actions

    /**
     * Adds a task
     * @param {TaskObject} task
     */
    addTask(task) { 
        this.tasks.push(Object.assign(task, { _added : true }));
    }

    /**
     * Removes a task
     * @param {TaskObject} task
     */
    removeTask(task) { 
        // find the task 
        let foundTask = this.find(task)

        if (foundTask) {
            // if the task there is _added
            if (foundTask._added) {
                // splice it out
                this.tasks.splice(taskIndex, 1)
            }
            // otherwise, mark it _deleted
            else { 
                task._deleted = true
            }
        }

    }

    revertTaskChanges(task) { 
        // find the task
        let foundTask = this.find(task)

        if (foundTask) { 
            // revert back to state of OriginalObject
            Object.assign(foundTask, foundTask.OriginalObject)
        }


    }





}

/* decorating the class; // have to do it this way because decorator syntax is in ES.next, which hasn't released yet. Also, I cannot seem to get it via Babel CDN link */
mobx.decorate(TasksStore, {
    tasks : mobx.observable,
    taskIndex : mobx.observable,
    tasksHaveChanged : mobx.computed,
    currentTask : mobx.computed,
    addTask : mobx.action,
    removeTask : mobx.action,
    setPermissionLinks : mobx.action,
    revertTaskChanges : mobx.action,
    saveTasks : mobx.action
})    

PayRatesStore

/**
 * @class
 * The state of the pay rates
 */
class PayRatesStore { 

    /**
     * @field {PayRate[]}
     */
    payRates = []

    payRateIndex = -1;

    _payRatesCache = new Map();

    /**
     * @param {TasksStore} tasksStore
     */
    constructor(tasksStore) { 
        this.tasksStore = tasksStore
    }

    // getters
    get currentPayRate() { 
        return this.payRates[this.payRateIndex];
    }

    set currentPayRate(newPayRate) { 
        this.payRates[this.payRateIndex] = newPayRate;
    }

    /**
     * Returns the index of payRate with respect to this list of PayRates
     * @param {PayRate} payRate 
     * @return {PayRate} the payRate, if found, or undefined
     */
    find(payRate) { 
        const stringID = `storeID:${payRate.StoreId};empID:${payRate.EmpId};taskID:${payRate.TaskId}`
        // Implementation for this is inspired by https://mobx.js.org/refguide/computed-decorator.html#computeds-with-arguments
        if (this._payRatesCache.has(stringID)) { 
            return this._payRatesCache.get(stringID).get()
        }
        const computedFind = computed(() => this.payRates.find((val) => (JSON.stringify(val) === JSON.stringify(payRate))))
        this._payRatesCache.set(stringID, computedFind)
        return computedFind.get()
    }

    // actions
    /**
     * Adds a PayRate
     * @param {PayRate} payRate 
     */
    addPayRate(payRate) { 
        this.payRates.push(Object.assign(payRate, { _added : true }))
    }

    /**
     * Edits the current PayRate
     * @param {PayRate} payRate 
     * @deprecated This may not be necessary. Just update the PayRate from the caller!
     */
    editPayRate(payRate) { 
        this.currentPayRate = payRate
    }

    /**
     * Removes a payRate at index
     * @param {number} index 
     */
    removePayRateAt(index) {
        // if this payrate has been _added, actually remove it
        let foundPayRate = this.payRates[index]
        if (foundPayRate._added) { 
            this.payRates.splice(index, 1)
        }
        // otherwise, mark it deleted
        else { 
            foundPayRate._deleted = true
        }
    }

    /**
     * Undo removal of PayRate
     * @param {number} index 
     */
    undoRemovePayRateAt(index) { 
        // if it is marked _deleted, revert that
        let foundPayRate = this.payRates[index]
        if (foundPayRate._deleted) {
            delete foundPayRate._deleted
        }
    }



}

mobx.decorate(PayRatesStore, {
    payRates : mobx.observable,
    payRateIndex : mobx.observable,
    currentPayRate : mobx.computed,
    addPayRate : mobx.action,
    editPayRate : mobx.action,
    removePayRateAt : mobx.action,
    undoRemovePayRateAt : mobx.action
})

PermissionsStore

/**
 * @class
 * The state of the permissions part of the Tasks page
 */
class PermissionsStore { 

    permissionLinkList = [];

    /**
     * @param {TasksStore} tasksStore
     */
    constructor(tasksStore) { 
        this.tasksStore = tasksStore
    }

    get permissionLinks() { 
        return this.permissionLinkList;
    }

    /**
     * Sets this store's permission links
     * @param {PermissionLink[]} permissionLinks 
     */
    set permissionLinks(permissionLinks) { 
        this.permissionLinkList = permissionLinks;
    }



}

mobx.decorate(PermissionsStore, {
    permissionLinks : mobx.computed
})

Проблема

Когда я иду вПопробуйте это в консоли с помощью:

tasksStore = new TasksStore () newTask = new TaskObject (...) // некоторая фиктивная задача или некоторая очищенная задача tasksStore.add (newTask)

Я заметил, что this.tasks === ... печатает только один раз!Еще хуже: магазины ставок оплаты и разрешений не получают данные из той задачи, которая была добавлена!

Как мне по-настоящему связать дочерние магазины ??

...