Code Monkey home page Code Monkey logo

minsto's Introduction

Minsto

A mini store for javascript/React app

Features

  1. Zero configuration
  2. No boilerplate
  3. Extensive TypeScript support
  4. Global, shared, or component level stores
  5. Plugins supported
  6. React Suspense supported
  7. Future action listening supported
  8. Cancellable action dispatching supported
  9. React hooks based API
  10. Computed properties (support async mode) - i.e. derived data
  11. Data fetching / side effects
  12. Local store supported
  13. React Native supported
  14. Hot Reloading supported
  15. Reactotron supported

Get Started

Installation

npm install minsto --save

Step 1 - Create your store

import minsto from "minsto";

const todoStore = minsto({
  state: {
    items: ["Create store", "Use the store"],
  },
  actions: {
    add(store, payload /* item title */) {
      // state.items is immutable
      // we must use array.concat to create new copy of items and append new item at end
      store.items = store.items.concat(payload);
    },
  },
});

Step 2 - Use the store

import React, { useRef } from "react";
import todoStore from "./todoStore";
import useStore from "minsto/react";

function TodoList() {
  const inputRef = useRef();
  const { todos, add } = useStore(todoStore, (store) => {
    return {
      todos: store.items,
      add: store.add,
    };
  });
  return (
    <div>
      <input ref={inputRef} />
      <button onClick={() => add(inputRef.current.value)}>Add</button>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>{todo}</li>
        ))}
      </ul>
    </div>
  );
}

The useStore hook has the following signature.

const result = useStore(CounterStore, (store) => {
  return {
    count: store.count,
    increase: store.increase,
  };
});
console.log(result.count, result.increase);

The hook accepts a storeMapper function. The storeMapper function will be provided your input store and should return the slice of state or actions required by your component. Anytime an update occurs on your store the storeMapper function will be executed, and if the newly mapped result does not equal the previously mapped result your component will be rendered with the new value.

Using actions to update state

In this section we will tell you how to update store state

Defining actions on our model

We are going to define two actions on our counterStoreModel; one to increase count state, and another to decrease count state. Action is pure function that retrieves two arguments (store object and action payload)

const counterStoreModel = {
  state: {
    count: 0,
  },
  // all action definitions must be placed inside actions block
  actions: {
    increase(store, payload) {
      store.count++;
    },
    decrease(store, payload) {
      store.count--;
    },
  },
};

Because action is pure function, you can write unit test easily

test("increase", () => {
  const state = { count: 0 };
  counterStoreModel.actions.increase(state);
  expec(state.count).toBe(1);
});

Using component store

Counter App with custom store hook

Computed Properties

Computed properties are the perfect candidate to help us clean up the more advanced state mapping that is happening within some of our application's components. Let's refactor each derived data case.

First up, let's add a computed property to represent the total todo items.

const todoModel = {
  state: {
    todos: [
      { id: 1, title: "todo 1", completed: false },
      { id: 2, title: "todo 2", completed: true },
    ],
  },
  computed: {
    total: (state) => state.todos.length,
  },
};

Next up, we will add a computed property to represent the completed todos and active todos.

const todoModel = {
  state: {
    todos: [
      { id: 1, title: "todo 1", completed: false },
      { id: 2, title: "todo 2", completed: true },
    ],
  },
  computed: {
    total: (state) => state.todos.length,
    completed: (state) => state.todos.filter((todo) => todo.completed).length,
    active: (state) => state.todos.filter((todo) => !todo.completed).length,
  },
};

Computed properties optionally allow you to provide an array of state resolver functions as the first argument to the computed property definition. These state resolver functions will receive the state that is local to the computed property, as well as the entire store state, and allow you to resolve specific slices of state that your computed function will take as an input.

const todoModel = {
  state: {
    todos: [
      { id: 1, title: "todo 1", completed: false },
      { id: 2, title: "todo 2", completed: true },
    ],
  },
  computed: {
    total: (state) => state.todos.length,
    completed: (state) => state.todos.filter((todo) => todo.completed).length,
    active: (state) => state.todos.filter((todo) => !todo.completed).length,
    // show todo list stats
    stats: [
      // named computed properties / state resolvers
      "total",
      "completed",
      "active",
      (total, completed, active) => ({ total, completed, active }),
    ],
  },
};

Local Store

If you don't want to mess many things into the global state, you can use local store to split app logics that used to only specified component. The local store instance stored in host component. It will be removed when its host component is unmounted. A host component can contain many local store, local store model can be reused by many components.

import { useLocalStore } from "minsto/react";
const CounterModel = {
  state: {
    count: 0,
    step: 1,
  },
  actions: {
    increase(store) {
      store.count += store.step;
    },
  },
};
const Counter = ({ step = 1 }) => {
  const store = useLocalStore(CounterModel);
  // pass step prop to store
  store.step = step;

  return <h1 onClick={store.increase}>{store.count}</h1>;
};

Working with Development Tools

Reactotron

Install reactotron-redux

npm install --save-dev reactotron-redux

Configuring

import Reactotron from "reactotron";
import { reactotronRedux } from "reactotron-redux";
import minsto from "minsto";
import { connectReactotronRedux } from "minsto/reactotron";
const reactotron = Reactotron.configure({ name: "React Native Demo" })
  .use(reactotronRedux())
  .connect();

const storeEnhander = reactotron.createEnhancer();
const counterStore = minsto({
  state: { count: 0 },
  actions: {
    increase: (store) => store.count++,
  },
});

connectReactotronRedux(storeEnhander, counterStore);

Examples

  1. Counter App
  2. Counter App using Local Store
  3. Todo App
  4. Movie Search using Async Computed Prop
  5. Animation using GSAP

minsto's People

Contributors

linq2js avatar

Stargazers

 avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.