1.0.0-rc.4 • Published 5 years ago

lemon-state v1.0.0-rc.4

Weekly downloads
4
License
ISC
Repository
github
Last release
5 years ago

logo

lemon-state

Very simple state manager. No action constants, providers and HOCs, only one function in component. The component updates only when update used properties. Type-safe checked with state types.

install

npm i lemon-state

usage

Create file with initialing of store:

AppStore.js

import { LemonState, createHook } from "lemon-state";

const initialState = {
  loading: true, // static data
  lang: "ru",
  data: [],
  computed: (state) => state.data.join(":") // computed data, just use here this state or anoter, all depends will be resolved
};

const actions = {
  onSetLoad: (_, payload) => ({
    loading: payload
  }),
  onSwitchLang: ({ getState }, payload) => ({
    lang: getState().lang === "ru" ? "en" : "ru"
  }),
  onAsyncAction: async ({ getState, setState }, payload) => {
    setState({ loading: true });
    const data = await loadData(payload);
    setState({ loading: false, data });
  })
};

const config = {
  name: "AppStore",
  debug: process.env.NODE_ENV === "development" // if we want using redux dev tools
};

export const store = new LemonState(initialState, actions, config); // create store
export const { useStore } = createHook(store); // create hook

initialState - your first state and this is scheme for autocompletion, write here all properties, object expected actions - object with actions, it is scheme for autocompletion too config - object with config for debugging

Action expect function with two arguments:

1) StoreChange - object with four properties for working with store: setState, getState and dispatch: setState - update state and rerender all components which used updated (only!) variables getState - return actual state dispatch - for dispatching another actions 2) payload - any data for working which you call action

Action function can return plain object, it update state.

LemonState expect two arguments initialState, actions. actions is not required, but must be object:

class LemonState {
  // get actual state
  getState: () => State;

  // method expect function which be called when state will be updated, return unsubscribe callback
  subscribe: (fn) => Unsubscribe;

  // after first call subscriber know what properties it using, other call will be only after depends changes
  smartSubscribe: (fn) => Unsubscribe;

  // set new state, call subscribers only after check different by Object.is algorithm
  setState: (diff) => void;

  // call action function with StoreChange object
  dispatch: (action) => any;

  // clear all memory of Store
  remove: () => void;

  // object with dispatched actions
  actions: Actions;

  // removed indicator
  isRemoved: boolean;
}

useStore - hook for react component. Return your store with actions. If you once get something property, when it property change component will update.

Loading.js

import React, { memo } from "react";
import { useStore } from "./AppStore";

const Language = memo(() => {
  const { lang, onSwitchLang, onSetLoad } = useStore();
  console.log("Loading render");
  return (
    <div>
      <button onClick={onSwitchLang}>change language</button>
      <button onClick={() => onSetLoad(false)}>change loading</button>
      <div>language: {lang}</div>
    </div>
  );
});

export default Language;

Language.js

import React, { memo } from "react";
import { useStore } from "./AppStore";
const Loading = memo(() => {
  const { loading } = useStore(); // if you get 'loading' prop, only this prop will update component
  console.log("Language render");

  return <div>{loading ? "loading" : "loaded"}</div>;
});

export default Loading;

App.js

import React, { memo } from 'react';
import Language from './Language';
import Loading from './Loading';

export default () => (
  <div>
    <Language />
    <Loading />
  </div>
);

See example in CodeSandbox

What we see in console: 1) Components first render:

Language render 
Loading render 

2) Click to 'Change language' Button, the both components updated, but Loading component doesn't use lang property. It was because first update of store fire updating all dependent components In console appended:

Language render
Loading render

3) Click to 'Change language' Button only Language update forever In console appended:

Language render 

4) Click to 'change loading' Button only Loading update In console appended:

Loading render 

You don't need use constants, HOCs and another verbose patterns. Only one function do all you need: data access and action dispatching

You can use several Stores, like this:

SettingsStore.js

import { LemonState } from "lemon-state";
import { store as appStore } from "./AppStore";

const initialState = {
  userSettings
};

const actions = {
  onSetSettings: ({ state }, payload) => {
    if (!appStore.getState().loading) {
      return {
        userSettings: payload
      };
    }
  }
};

export const store = LemonState(initialState, actions);

If you use IDE, autocompletion will help you autocompletion

Typescript

You can use helper Types import { Actions } from 'lemon-state'. See src/__tests__/TestApp example.

Debug

You can use redux-devtools-extension

const config = {
  name: 'AppStore',
  debug: process.env.NODE_ENV === 'development'
};

export const store = store(initialState, actions, config);