Skip to main content

Usage

Now we are going to build our accessor from its components.

Helpers can be imported from mobx-accessor:

import {
actionTree,
getterTree,
mutationTree,
makeAccessor,
} from 'mobx-accessor';

State

State is just an object holding the data needed for the view, usually retrieved from backend. We must define it with all possible fields.

const defState = () => ({
value: 1,
});

All fields in state will be accessible as getters from the accessor.

In this example, the state has only one field value which is an integer. The types can be inferred from the definition, so we don't have to declare it explicitly. The final accessor will have a getter:

// Automatically created
interface ExampleAccessor {
readonly value: number;
}

Mutations

Mutations are functions that modify the state synchronously. It is only allowed to modify data through mutations in mobx-accessor.

We use the mutationTree helper to infer the types.

const defMutations = mutationTree({ state: defState }, {
increase(state) {
state.value++;
},
addUp(state, delta: number) {
state.value += delta;
},
});

Each function can have at most 2 parameters, the first being the current state, and the second being the payload called with this mutation.

In the example above, we will add two functions to the accessor:

// Automatically created
interface ExampleAccessor {
increase(): void;
addUp(delta: number): void;
}

Getters

Getters are values computed from state or other getters. Thanks to MobX, they will always be updated automatically if any of their dependencies are changed.

We use the getterTree helper to infer the types.

const defGetters = getterTree({ state: defState }, {
double(state) {
return state.value * 2;
},
doubleAgain(state, getters) {
const double = getters.double as number;
return double * 2;
},
});

Each function can have at most 2 parameters, the first being the current state, and the second being an object of all available getters.

Note:

  • The type of the second parameter getters cannot be inferred as it depends on the type of itself.
  • Getters are supposed to derive values immediately, without the ability to modify the state, thus have no access to mutations or actions.

In the example above, we will add two getters to accessor:

// Automatically created
interface ExampleAccessor {
readonly double: number;
readonly doubleAgain: number;
}

Actions

Actions are functions dealing with the logic. It can be either synchronous or asynchronous.

We use the actionTree helper to infer the types.

const defActions = actionTree({
state: defState,
getters: defGetters,
mutations: defMutations,
}, {
async rock({ state, getters, mutations }) {
// await is allowed
await doStuff();
// change value through `mutations`, don't modify `state` directly
mutations.increase();
// check the latest values
console.log(state.value, getters.double);
},
rockPayload({ mutations }, delta: number) {
mutations.addUp(delta);
return delta;
},
});

Each function can have at most two parameters, the first being an object of the current { state, getters, mutations }, and the second being the payload called with this action.

Note:

  • Actions have access to the current state, getters and mutations.
  • However, actions cannot modify state directly. Modification must be done through mutations so that MobX can easily get notified of the changes.

In the example above, we will add two functions to the accessor:

// Automatically created
interface ExampleAccessor {
rock: () => Promise<void>;
rockPayload: (delta: number) => number;
}

Accessor

Now let's build the accessor with makeAccessor:

const accessor = makeAccessor({
state: defState,
mutations: defMutations,
getters: defGetters,
actions: defActions,
});

Now we can access everything we defined earlier from the accessor:

// typeof accessor
interface ExampleAccessor {
// From State
readonly value: number;
// From Getters
readonly double: number;
readonly doubleAgain: number;

// From Mutations
increase(): void;
addUp(delta: number): void;
// From Actions
rock: () => Promise<void>;
rockPayload: (delta: number) => number;
}

Note:

  • Since all properties will be merged into one accessor, no duplicate names are allowed.