Dispatching some people

Here's one way to set the firstName property of a person in position 0. It's pure Redux, although the action format has a fussy structure:

store.dispatch({
    type: "AT",
    payload: {
        key: 0,
        action: {
            type: "FIRST_NAME", 
            payload: { 
                type: "REPLACE", 
                payload: "John" 
            }
        }
    }
});

See how it's nested? Actions inside actions. Each action has type and payload, and sometimes the payload contains a further action. We call this an action path. It just naturally emerges from the structure of the data model.

Try it in index.tsx. Also, try changing "John" into 3.14 so it's the wrong type. Or misspell one of the action names. TypeScript won't let you do that.

We can do it with cursors instead. Add these to your imports:

import { snapshot, replace } from "immuto";
import { Person } from "./Person";

And try:

snapshot(store)
    .$(People.at(0))
    .$(Person.lastName)
        (replace("Zoidberg"));

The snapshot function gets a cursor for the current state of the store. It's the Immuto way of doing store.getState(), except it also retains the power of dispatch. As it's a cursor, we can then take the path of our choice down the data structure.

The result is a function that we can directly call to dispatch an action. It is a custom dispatch function. replace is a good old action creator, just like in Redux (remember: Immuto is Redux). So we we could have said:

snapshot(store)
    .$(People.at(0))
    .$(Person.lastName)
        ({ type: "REPLACE", payload: "Zoidberg" });

Again, any typo such as type: "REPLOCE" is caught instantly. And try piping in the wrong order:

snapshot(store).$(Person.firstName) // error: Person[] is not assignable to Person

Our store's internal state is of type People so it's just a plain ol' array of objects:

const example: People = [
    { firstName: "Homer",  lastName: "Simpson", trusted: false },
    { firstName: "Marge",  lastName: "Simpson", trusted: true  },
    { firstName: "Bart",   lastName: "Simpson", trusted: false },
    { firstName: "List",   lastName: "Simpson", trusted: true  },
    { firstName: "Maggie", lastName: "Simpson", trusted: false },
]

So I guess we could copy it into the store like this:

for (let p = 0; p < example.length; p++) {
    const person = snapshot(store).$(People.at(p));

    person.$(Person.firstName)(replace(example[p].firstName));
    person.$(Person.lastName)(replace(example[p].lastName));
    person.$(Person.trusted)(replace(example[p].trusted));
}

On the other hand, it would be nice if we could just throw the whole example straight in the store. Let's do something completely off-the-wall and define an action! Go into People.ts and fix your immuto import so it says:

import { reducer, array, action } from "immuto";

Then between the at and reduce declarations, add:

export const initialize = action("INITIALIZE", 
    (state: People, payload: People) => payload);

See how we have to write a kind a partial reducer? We just get given the current state and the payload of our action. The state needs to be the right type for the object, in this case People, but the type of payload could be anything we want (usually something JSON-compatible though. In this case we set the payload to be of type People so we can simply return it as the new state, throwing away the old state.

Finally, tack it on to the reducer:

export const reduce = reducer(empty)
        .action(at)
        .action(initialize);

With this added ingredient, all we need in index.tsx is:

store.dispatch(People.initialize(example));

People.initialize is an action creator. We just pass it the payload. Again, to be super clear, we could have said:

store.dispatch({ type: "INITIALIZE", payload: example });

So in summary, this is all index.tsx now needs to contain:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { bindToStore } from "immuto-react";
import { People } from "./People";
import { PeopleEditor } from "./PeopleEditor";

const store = People.reduce.store();

const App = bindToStore(store, p => <PeopleEditor people={p} />);

ReactDOM.render(<App/>, document.querySelector("#root"));

const example: People = [
    { firstName: "Homer",  lastName: "Simpson", trusted: false },
    { firstName: "Marge",  lastName: "Simpson", trusted: true  },
    { firstName: "Bart",   lastName: "Simpson", trusted: false },
    { firstName: "Lisa",   lastName: "Simpson", trusted: true  },
    { firstName: "Maggie", lastName: "Simpson", trusted: false },
]

store.dispatch({ type: "INITIALIZE", payload: example });

Let's give it a whirl:

$ webpack

Open index.html in your browser and try changing the names of the Simpsons family. It's really awesome.

results matching ""

    No results matching ""