Skip to content

hrsh7th/srimmer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Srimmer

Srimmer provides simple api to use react, immer and TypeScript.

inspired react-copy-write.

API

import { define, Select } from "srimmer";

/**
 * Extract consumer's selected state. `Select<typeof Consumer>`
 */
export { Select };

/**
 * Your state.
 */
type State = {
  todos: {
    name: string;
    status: string;
  }[];
};

/**
 * Define some state utilities from your state type.
 */
const defined = define<State>();

export const {
  /**
   * State provider.
   * @type {React.ComponentType<{ state: State; }>}
   */
  Provider,

  /**
   * State updater.
   * @type {(updater: (state: State) => void) => void}
   */
  update,

  /**
   * immer's patchListener.
   * @type {(patchListener: PatchListener) => void}
   */
  patches,

  /**
   * State selector.
   * @type {<T>(select: (state: State) => T) => Consumer<T>}
   */
  select,

  /**
   * State getter.
   * Note: Carefully. It makes implicit depends to state values.
   * @type {() => State}
   */
  get,

  /**
   * State setter.
   * Note: For testing.
   * @type {(state: State) => void}
   */
  set
} = defined;

Real World Usage

define your state (src/state/index.ts)

import { define, Select } from 'srimmer';

export type State = { ... };

export const {
  Provider,
  select,
  update,
  patches,
  get,
  set
} = define<State>();

export { Select };

define your action (src/action/index.ts)

import { update } from "../../state";

export const addNewTask = () => {
  update(state => {
    state.todos.push({
      name: `new todo ${state.todos.length}`,
      status: "todo"
    });
  });
};

bootstrap (src/index.tsx)

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "./state";

ReactDOM.render(
  <Provider state={createInitialState()}>
    <App />
  </Provider>,
  document.getElementById("app")!
);

function createInitialState() {
  return JSON.stringify(document.getElementById("app")!.getAttribute("data"));
}

consume state (src/component/*/.tsx)

import { select, Select } from '../../state';
import { addNewTask } from '../action';

const Consumer = select(state => ({
  todos: state.todos
}));

export default () => (
  <Consumer>
    {state => (
      <button onClick={() => onAddButtonClick(state)}>add</button>
      <div>{todos(state)}</div>
    )}
  </Consumer>
);

const todos = (state: Select<typeof Consumer>) => {
  return state.todos.map(todo => (
    <div key={todo.id}>{todo.name} - {todo.status}</div>
  ));
}

const onAddButtonClick = () => {
  addNewTask();
};

testing your updater (test/action/index.test.ts)

import diff from 'snapshot-diff'; # https://github.com/jest-community/snapshot-diff
import { set, get } from '../../../src/state';
import { addNewTask } from '../../../src/action';

beforeEach(() => {
  set({ ...fixture });
});

test('addNewTask', () => {
  const state = get()!;
  addNewTask();
  expect(diff(state, get()!)).toMatchSnapshot();
});

Recommended Structure

/src
  /state      # State schemas and querying utility functions.
    index.ts
  /action     # State updators.
    index.ts
  /component  # State selectors.
    index.tsx
  index.tsx   # Bootstrap.

See hrsh7th/ganttcharty.

About

Srimmer provides simple api to use react and immer.

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published