Overmind

✋ I'm Etienne

Mental model

In Programming

Libraries help us shape
the mental model of our apps

Naming is important

Overmind Mental model

State

Derive

Effects

Action

Context

Mutation

const myAction = async ({
  api,
  state,
  value: query
}) => {
  state.isLoading = true;
  const result = await api.getResult(query);
  state.result = result;
  state.isLoading = false;
};

// the the view
myAction("this-is-the-query");

Pipe Action

Pipe Action

Operations

Pipe Action

op-op-spec

function operator (err, value, next, final = next) {}

Pipe Action

Operators

Pipe Action

Operators

const mutate = (
  doMutation: (context: Context) => void
): Operator => {
  // ...
}

Pipe Action

Operators

const setIsLoading = mutate(({ state, value }) => {
  state.isLoading = value
})
function pipe(...operators) {
  return (err, context, next, final = next) => {
    if (err) next(err);
    else {
      // run each operator one after the other
      // if an operator return a Promise
      // wait for the Promise to resolve (or reject)
      // before moving to the next
    }
  };
}
pipe(
  setLoading,
  fetchQueryResult,
  setQueryResult,
  setNotLoading
);

Devtools 🚀

Running the Devtools

npx overmind-devtools

Move fast, break things 🏎

Do we have time for more API ?

type Todo = {
  id: number;
  content: string;
  done: boolean;
};

type State = {
  todos: Array<Todo>;
};

const state: State = {
  todos: [
    {
      id: 1,
      content: "Do this first",
      done: false
    }
  ]
};
import { Derive } from "overmind";

const todoCount: Derive<number> = state =>
  state.todos.length;

type State = {
  todos: Array<Todo>;
  todoCount: Derive<number>;
};

const state: State = {
  todos: [],
  todoCount
};
async function fetchData(
  param: string
): Response {
  // ...
}

const effects = {
  fetchData
};
import { Action } from "overmind";

export const createTodo: Action<string> = async ({
  value,
  createId,
  state,
  getRandomColorAsync
}) => {
  const color = await getRandomColorAsync();
  const newTodo = {
    id: createId(),
    color,
    content: value,
    done: false
  };
  state.todos.push(newTodo);
};

const actions = {
  createTodo
};
import { pipe, Pipe, debounce } from "overmind";
import {
  setQuery,
  filterIsQueryNotEmpty,
  setResultFetching,
  fetchResult,
  setResult
} from "./operations";

const onQueryInputChangeAction: Pipe<
  string
> = pipe(
  setQuery,
  debounce(200),
  filterIsQueryNotEmpty,
  setResultFetching,
  fetchResult,
  setResult
);

const actions = {
  onQueryInputChangeAction
};
// operations.ts
import { map, filter, mutate } from "overmind";

export const filterIsQueryNotEmpty = filter(
  ({ value }) => value !== ""
);

export const fetchResult = map(
  ({ api, value: query }) =>
    api.fetchResult(query)
);

export const setQuery = mutate(
  ({ state, value: query }) => {
    state.query = query;
  }
);
import { Overmind, TApp } from "overmind";

const config = {
  state,
  effects,
  actions
};

declare module "overmind" {
  interface IApp extends TApp<typeof config> {}
}

export const app = new Overmind(config);
import {
  TConnect,
  createConnect
} from "overmind-react";

const app = new Overmind(config);

export type Connect = TConnect<typeof app>;

export const connect = createConnect(app);
import * as React from "react";
import { connect, Connect } from "../../app";

const AddTodo: React.SFC<Connect> = ({ app }) => (
  <div>
    <form
      onSubmit={event => {
        event.preventDefault();
        app.actions.addTodo();
      }}
    >
      <input
        placeholder="I need to..."
        value={app.state.newTodoTitle}
        onChange={app.actions.changeNewTodoTitle}
      />
      <button disabled={!app.state.newTodoTitle}>
        add
      </button>
    </form>
  </div>
);

export default connect(AddTodo);

That's it, thanks

PS: I'm on 🐦 @Etienne_dot_js