@teasenshi/fission
TypeScript icon, indicating that this package has built-in type declarations

3.0.4 • Public • Published

Fission

build: status codecov: percent commitizen: friendly code style: prettier semantic-release linter: eslint docs: gh-pages npm (scoped)

State management library for ES5+ environments designed for ease of use.

Recommended IDE

You should be using Visual Studio Code because its simple, fast, extensible and beloved by many developers.

Make sure to install all the recommended extensions that come with the repository for the best possible coding experience.

NPM Scripts

Note that these examples use yarn but you can use the equivalent npm run <command> instead.

Most of them will automatically run when you perform certain actions on your repository.

Code style

Ensures code consistency in your code base.

These commands automatically get run before commits.

  • yarn style - Runs all style:* commands.
  • yarn style:lint - Lints your code using eslint.
  • yarn style:format - Formats your code using prettier.

Build Tasks

Creates builds from your Typescript files for CommonJS (cjs) and ES6 modules (esm).

  • yarn build - Runs all build commands which creates builds for different node environments.
  • yarn build:main - Creates a build using cjs modules.
  • yarn build:module - Creates a build using esm modules.

Testing

Ensures code is reliable by running your jest unit tests.

Unit tests automatically get run before commits.

  • yarn test - Runs all tests and generates a code coverage report.
  • yarn test:watch - Watches file changes and reruns tests for those changed files.

Code Coverage

Generates and publishes documentation based on your typedoc comments.

  • yarn cov - Generate a code coverage report.
  • yarn cov:open - Open generated code coverage report.
  • yarn cov:publish - Publish generated code coverage reports to codecov. Running this command locally will require the upload token for e.g yarn cov:publish --token="YOUR_TOKEN_HERE"

Documentation

Generate and publishing documentation based on your typedoc comments.

  • yarn doc - Generates documentation from code.
  • yarn doc:open - Opens generated documentation in your default browser.
  • yarn doc:publish - Publishes generated documentation.

Helpers

These commands perform misc tasks.

  • yarn commit - Create a new commit using the commitizen cli.
  • yarn clean - Cleans up all build artifacts such as the distribution folder.

Conventional Commits

Commit messages to this repository that don't follow the conventional commit guidelines will be rejected by a commit-msg git hook.

No one likes rejection so use the yarn commit script which provides a CLI interface for creating formated commits.

Git Hooks

If you would like to run custom tasks during important actions on a repository you can use git hooks.

To make this as simple as possible we use husky which is also used in the conventional commits toolchain.

Debugging

The following launch configurations will assist with debugging your library.

  • Current TS File - debug current typescript file.
  • Current Jest Test - debug current jest test.
  • All Jest Tests - debug all jest tests.

Usage

Creating a store instance

There are 2 methods for creating a store instance. The one you end up using depends on whether or not you are using typescript.

import Store from '@teasenshi/fission';

// Use in javascript environments
const store = new Store(options);

// Use in Typescript environments which provides proper type support on the store instance
const store = Store.create(options);

Store state

Store state is readonly data stored on the store instance. You specify store state by adding a state property in the options object passed to the store constructor.

const store = new Store({
  state: {
    price: 55,
    qty: 10,
    shouldFormat: false,
    total() {
      return this.price * this.qty;
    },
    totalTimesFive() {
      return this.total * 5;
    },
    formattedTotal() {
      if (this.shouldFormat) {
        // return a formatted currency string
        return new Intl.NumberFormat('en-US', {
          style: 'currency',
          currency: 'USD',
        }).format(this.total);
      } else {
        return 'formatting disabled';
      }
    },
    shortCircuiting() {
      return this.price > 60 && this.qty > 20;
    },
  },
});

console.log(store.$state); // output: { price: 55, qty: 10, total: 550, totalTimesFive: 2750, formattedTotal: 'formatting disabled',  }

// This line throws an exceptions as store state cannot be set directly.
store.$state.price = 55;

Computed Properties

The function definitions in the example above are computed properties/values. When you use other properties in a computed property definition they are registered as dependencies. Changing dependencies causes a computed property to automatically reevaluate.

Chained Computed Properties

totalTimesFive is a chained computed property. When total changes, i.e when price and/or qty change, it will cause totalTimesFive to change as well.

The order in which you declare them are important. For example declaring totalTimesFive before total will not work since total is undefined when totalTimesFive is created!

Caveats

formattedTotal demonstrates an important consideration when creating computed properties.

Since both shouldFormat and total are used in that function you would expect that changing either would cause it to be reevaluated.

Unfortunately this is not the case since the first time this function runs shouldFormat is false and the function will never reach total. In this case only changes to shouldFormat will cause it to be reevaluated.

Issues like these can be solved by refactoring the function in a way that all properties are reachable the first time it runs.

In formattedTotal you can for example add total to the if condition if(this.total && this.shouldFormat).

shortCircuiting demonstrates the same issue due to conditional short circuiting.

In this case, changes to qty will not cause shortCircuiting to be reevaluated. This is because the first time this function is evaluated price < 60 so the condition short circuits and qty is never reached.

Mutations

Mutations are synchronous functions that have the ability to change the store state. They are committed (run) using the store.$commit method.

const store = new Store({
  state: {
    ...
  },
  mutations: {
    /*
     'context' is an object containing a reference to some store instance members.
     'payload' is any value that can be passed to the mutation by the user.
    */
    setPrice(context, payload) {
      context.$state.price = payload;
    },
    // Destructured context variable
    setQty({ $state }, value) {
      $state.qty = value;
    },
     // Mutations can return a value to the caller
    doSomethingAndReturn({ $state }, value) {
      // Do something...

      return value;
    }
    // The context parameter also contains a reference to the $commit function.
    doSomethingAndCommit({ $state, $commit }, value) {
      // Do stuff

      // Commit another mutation
      $commit('setQty', value);
    }
    // Mutations cannot asynchronously change store state
    invalidAsync({ $state }, value) {
      $state.price = 100; // Ok
      setTimeout(() => {
        $state.qty = value; // This line will cause an exception
      }, 100);
    }
  }
});

// Simple commit
store.$commit('setPrice', 22);
console.log(store.$state.price); // output: 22

// Store the result of committing the mutation
const result = store.commit('doSomethingAndReturn', 'A VALUE');
console.log(result); // output: "A VALUE"

Actions

Actions are functions that can be used to control some process or workflow, often asynchronously. They are dispatched (run) using the store.$dispatch method.

Actions cannot set store state directly but are able to call commits from within them to achieve the same result.

const store = new Store({
  state: {
    ...
  },
  mutations: {
    ...
  },
  actions: {
    // State is readonly in an action.
    invalidAction({ $state }, value) {
      $state.price = 100; // This line will cause an exception
    },
    // You can commit and dispatch from an action
    setTotal({ $commit, $dispatch }, payload) {
      // Do something with payload perhaps asynchronously persisting it to a server

      // Persist data to store using commit
      $commit('setTotal', payload);

      // Trigger another action
      $dispatch('totalSet');
    },
    /*
      Actions can also return values to the caller.
      You can use this capability to make them asynchronous using ES6 promises or generators
    */
    doSomethingAsync(context, payload) {
      return new Promise((resolve, reject) => {
        try {
          // Do something
          resolve();
        }
        catch {
          reject();
        }
      });
    }
  }
});

// Simple action
store.$dispatch('setTotal', {price: 50, qty: 15});

// Promise based action
store.dispatch('doSomethingAsync').then(value => {
  // Do something
});

Watchers

Watchers are functions that get called when a state property changes. You can add/remove watchers from properties using store.$watch and store.$unwatch respectively.

const store = new Store({
  state: {
    products: {
      stock: [
        { name: 'jelly', price: 20, qty: 100 },
        { name: 'mouse', price: 55, qty: 5 },
      ],
      lowStockItems() {
        return stock.filter((x) => x.qty < 10).map((x) => x.name);
      },
    },
  },
});

/*
  The following example watches a nested property.
  When that property changes the watcher function will get called.

  $watch expects the path to the property as a first parameter and the watcher function as a second 
  and returns a reference to the watcher function.
*/
const watcher = store.$watch('products.lowStockItems', (value, oldValue) => {
  console.log(value, oldValue);
});

// Remove 95 jelly from stock, jelly should now be a `lowStockItem` and the watcher will be called.
store.commit('removeStock', { name: 'jelly', amount: 95 }); // output: ['jelly', 'mouse'] ['jelly']

// Stop watching changes to 'lowStockItems'
store.$unwatch('products.lowStockItems', watcher);

// Since watcher is removed there is no output
store.commit('addStock', { name: 'jelly', amount: 95 });

Modules

Modules are a way to define nested store instances.

const store = new Store({
  // Root store instance
  state: {...},
  mutations: {...},
  actions: {...},
  modules: {
    // Factory module
    factory: {
      state: {...},
      mutations: {...},
      actions: {...},
      modules: {
        // Shop module
        shop: {
          state: {...},
          mutations: {...},
          actions: {...},
        }
      }
    }
  }
});

// Usage examples
// Root store
store.$state.name;
store.$commit('MUTATION_NAME', '');
store.$dispatch('ACTION_NAME', {});

// Root.Factory module
store.factory.$state.headCount;
store.factory.$commit('MUTATION_NAME', {});
store.factory.$dispatch('ACTION_NAME', '');

// Root.Factory.Shop module
store.factory.shop.$state.employees;
store.factory.shop.$commit('MUTATION_NAME', []);
store.factory.shop.$dispatch('ACTION_NAME', []);

Readme

Keywords

none

Package Sidebar

Install

npm i @teasenshi/fission

Weekly Downloads

0

Version

3.0.4

License

MIT

Unpacked Size

152 kB

Total Files

59

Last publish

Collaborators

  • amaya-loves-tea