{"id":16783561,"url":"https://github.com/tkh44/smitty","last_synced_at":"2025-04-09T21:18:31.986Z","repository":{"id":57364234,"uuid":"79427091","full_name":"tkh44/smitty","owner":"tkh44","description":"Tiny flux implementation built on mitt","archived":false,"fork":false,"pushed_at":"2017-04-26T15:52:19.000Z","size":1250,"stargazers_count":207,"open_issues_count":0,"forks_count":13,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-09T21:18:26.276Z","etag":null,"topics":["flux","flux-architecture","mitt","smitty"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tkh44.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-19T07:24:58.000Z","updated_at":"2025-01-17T10:16:37.000Z","dependencies_parsed_at":"2022-09-13T21:00:49.721Z","dependency_job_id":null,"html_url":"https://github.com/tkh44/smitty","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkh44%2Fsmitty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkh44%2Fsmitty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkh44%2Fsmitty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkh44%2Fsmitty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkh44","download_url":"https://codeload.github.com/tkh44/smitty/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248111973,"owners_count":21049578,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flux","flux-architecture","mitt","smitty"],"created_at":"2024-10-13T07:50:02.062Z","updated_at":"2025-04-09T21:18:31.963Z","avatar_url":"https://github.com/tkh44.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\" style=\"color: #343a40\"\u003e\n  \u003cimg src=\"https://cdn.rawgit.com/tkh44/smitty/master/smitty.png\" alt=\"smitty\" width=\"200\"\u003e\n  \u003cbr\u003e\n  smitty\n  \u003cbr\u003e\n  \u003cbr\u003e\n\u003c/h1\u003e\n\u003cp align=\"center\" style=\"font-size: 1.2rem;\"\u003eTiny flux implementation built on \u003ca style=\"color: #9B86FF;\" href=\"https://git.io/mitt\"\u003emitt\u003c/a\u003e\u003c/p\u003e\n\n# smitty\n\n[![npm version](https://badge.fury.io/js/smitty.svg)](https://badge.fury.io/js/smitty)\n[![Build Status](https://travis-ci.org/tkh44/smitty.svg?branch=master)](https://travis-ci.org/tkh44/smitty)\n[![codecov](https://codecov.io/gh/tkh44/smitty/branch/master/graph/badge.svg)](https://codecov.io/gh/tkh44/smitty)\n\n-   [Install](#install)\n-   [Basic Usage](#basic-usage)\n-   [Demos](#demos-v2)\n-   [Usage with Preact and React](#usage-with-preact-and-react)\n-   [API](#api)\n-   [Store](#store)\n-   [Action Creator Detailed Example](#action-creator-detailed-example)\n-   [Class As Reducer](#class-as-reducer)\n\n## Install\n\n```bash\nnpm install -S smitty\n```\n\n## Basic Usage\n```javascript\nimport { createStore } from 'smitty'\n\n// Create a store with initial state\nconst initialState = { count: 0 }\nconst store = createStore(initialState)\n\nstore.createActions({\n  add: 'count/ADD'\n})\n\n// add a reducer\nstore.handleActions({\n  [store.actions.add]: (state, e, type) =\u003e {\n    // increment foos by amount\n    return Object.assign({}, state, { count: state.count + e.amount })\n  },\n  '*': (state, e, type) =\u003e {\n    // '*' can be used for all kinds of fun stuff\n    console.log(e, type)\n    if (type === 'count/ADD') {\n      //...do something\n    }\n    return state\n  }\n})\n\nstore.actions.add({ amount: 5 })\n\nconsole.log(store.state)  // logs `{ count: 5 }`\n```\n---\n\n## Demos (v2)\n- [Photo Booth](https://tkh44.github.io/smitty/)\n  Demonstrates async api and saving parts of the store with [localforage](https://github.com/localForage/localForage)\n  - [source](https://github.com/tkh44/smitty/blob/master/demo/src)\n  \n## Demos (v1)\n- Basic\n  - [Object as state](http://codepen.io/tkh44/pen/zNNPPq)\n  - [Map as state](http://codepen.io/tkh44/pen/xgqBmO)\n  - [Immutable.Map as state](http://codepen.io/tkh44/pen/MJpREe)\n  - [Preact Router Example](http://codepen.io/seveves/pen/ryyNYz)\n- Async\n  - [Fetch api with Immutable.Map as state](http://codepen.io/tkh44/pen/JEWKJX)\n- Fun\n  - [UI colors with Immutable.Map as state](http://codepen.io/tkh44/pen/xgqNqy)\n  - [UI colors with custom class as state](http://codepen.io/tkh44/pen/OWmqBz)\n  - [Scroll sync](http://codepen.io/tkh44/pen/pReRVm)\n  - [Preact Component as state](http://codepen.io/tkh44/pen/bqabGX)\n\n---\n\n## Usage with Preact and React\n- Preact bindings - [preact-smitty](https://github.com/tkh44/preact-smitty)\n  \n  ```bash\n  npm install preact-smitty\n  ```\n\n- React bindings - [react-smitty](https://github.com/tkh44/react-smitty)\n  \n  ```bash\n  npm install react-smitty\n  ```\n\n---\n\n## API\n\n## `createStore(initialState: any)`\n\n#### Arguments \n\n`initialState: any` **required**: Determines the shape and initial state of your store. Can be of any type that you choose.\n\n#### Returns \n\n`Store: Store` **[Store](#store)**\n\n---\n\n## Store\n\n### emit: (_function_)\n\n**arguments**\n \n**type**: (_string_ | _function_)\n \n- [string], `type` determines which reducers are called.\n    \n    ```js\n    const store = createStore(0)\n    store.handleActions({\n      add: function (state, payload) {\n        return state + payload\n      }\n    })\n    console.log(store.state) // logs 0\n    store.emit('add', 1)\n    console.log(store.state) // logs 1\n    ```\n \n- [function] `type` becomes an action creator that is passed 1 argument\n    \n    - **store**: **[Store](#store)**\n    \n    This is useful to emit multiple actions from a single emit call.\n    \n    ```js\n    const store = createStore(0)\n    store.handleActions({\n      add: function (state, payload) {\n        return state + payload\n      }\n    })\n    function asyncAction (emit, state) {\n      emit('add', 1)\n      console.log(state) // logs 1\n      setTimeout(() =\u003e {\n        emit('add', 1)\n        console.log(state) // logs 3\n      }, 100)\n      emit('add', 1)\n      console.log(state) // logs 2\n    }\n        ```\n\n**payload**: (_any_) optional\n\npayload to pass to your reducer\n\n```js\nconst store = createStore({ name: 'Arrow' })\nstore.handleActions({\n  'update/NAME': function (state, payload) {\n    // I really don't care if you return a new state\n    // Nobody is judging. Do what your ❤️ tells you.\n    // Just be consistent\n    return Object.assign({}, state, payload)\n  }\n})\nconsole.log(store.state) // logs { name: 'Arrow' }\nstore.emit('update/NAME', { name: 'River' })\nconsole.log(store.state) // logs { name: 'River' }\n```\n\n### createActions(): (_function_)\n\n**arguments**\n \n**actionMap**: (_object_)\n    \nObject where key is the action creator's name and the value can be of type `string` or `function`.\n\nIf the value is a `string`, an action creator is attached to `store.actions` as a function that accepts one argument, `payload`.\n\n```js\nstore.createActions({\n  add: 'count/ADD'\n})\n\n// The following are functionally equivalent\nstore.actions.add(1)\n\nstore.emit('count/ADD', 1)\n\n```\n\nAction creators with a string value can be used as the key in your `actionMap` in `handleActions`.\n\n```javascript    \nstore.createActions({\n  add: 'count/ADD'\n})\n\n// add a reducer\nstore.handleActions({\n  [store.actions.add]: (state, e, type) =\u003e {\n    // increment foos by amount\n    return Object.assign({}, state, { count: state.count + e.amount })\n  }\n})\n\nstore.actions.add({ amount: 5 })\n\nconsole.log(store.state)  // logs `{ count: 5 }`\n```\n\n\nIf the value is a `function`, it must be a function that returns an action creator. For async action creators.\n\n```js\nstore.createActions({\n  add: (amount) =\u003e {\n    return (store) =\u003e {\n      setTimeout(() =\u003e {\n       store.emit('count/ADD', amount)\n      }, 16)\n    }\n  }\n})\n\nstore.actions.add(1)\n\n```\n\n### handleActions(): (_function_)\n\n**arguments**\n \n**handlerMap**: (_object_)\n \nObject with keys that correspond to action types passed to `emit`\n\nWhen an event is emitted and the key matches the type the reducer is invoked with 3 arguments.\n\n  - **state**: (_any_) the store's state getter\n  - **payload** (__any__) the payload that was emitted\n  - **type** (__string__) the type that was emitted\n\n```js\nconst store = createStore({ color: 'blue', hovered: false })\nstore.handleActions({\n  'merge': function (state, payload) {\n    return Object.assign({}, state, payload)\n  },\n  'overwrite': function (state, payload) {\n    return payload\n  },\n\n  // Could do the same in one\n  // If you really miss redux do this and put a switch statement\n  '*': function(state, payload, type) {\n    return type === 'merge' ? Object.assign({}, state, payload) : payload\n  }\n})\nconsole.log(store.state) // logs { color: 'blue', hovered: false }\nstore.emit('merge', { color: 'red' })\nconsole.log(store.state) // { color: 'red', hovered: false }\nstore.emit('overwrite', { color: 'green', hovered: true, highlighted: false })\nconsole.log(store.state) // { color: 'green', hovered: true, highlighted: false \n```\n\n### actions: (_object_)\n\nMap of all the actions created in `store.createActions`\n\nThis is convenient so that you do not have to deal with action imports across your app.\n\n### on: (_function_)\n\nConvenience shortcut for [mitt.on](https://github.com/developit/mitt/#on).\n\n### off: (_function_)\n \nConvenience shortcut for [mitt.off](https://github.com/developit/mitt/#off).\n\n---\n\n## Action Creator Detailed Example\n\nYou can pass a function to `emit` in order to create an action creator\n\n**[running example](http://codepen.io/tkh44/pen/JEWKJX)**\n\n```javascript\nimport { createStore } from 'smitty'\n\n// Create a store with initial state\nconst initialState = {}\nconst store = createStore(initialState)\n\n// add our reducer\nstore.handleActions({\n  'api/GET_ROOM': (state, { id, res }) =\u003e {\n    return {\n      ...state,\n      [id]: {\n        ...state[id],\n        ...res.data\n      }\n    }\n  }\n})\n\n// create our action creators\nconst actions = {\n  requestRoom (id) {\n    return async (emit, state) =\u003e {\n      emit('REQUEST_ROOM', { id, res: { data: { id } } })\n      const res = await window.fetch(`https://api.mysite.com/${id}`)\n      res.data = await res.json()\n      emit('REQUEST_ROOM', { id, res })\n    }\n  }\n}\n\n// When calling emit with a function argument, the function will be called with `emit` and `state` as arguments\nconst result = store.emit(actions.requestRoom('1a'))\n\n// Return whatever you like from your action creator\nconsole.log(result) // logs \"Promise {[[PromiseStatus]]: \"pending\", [[PromiseValue]]: undefined}\"\n\n// After the fetch call, `REQUEST_ROOM` is fired a second time with our response data\nresult.then(() =\u003e console.log(store.state)) // logs `{ 1a: { id: '1a', title: 'My Room' }``\n\n```\n\n## Class As Reducer\nReducers are iterated with `for (let type in reducer) {...}` with no `obj.hasOwnProperty` check so this works.\n\n```javascript\nconst store = createStore({ foo: 5 })\n\nclass HistoryReducer {\n  constructor (initialHistory = []) {\n    this.history = createStore(initialHistory)\n    this.history.handleActions({\n      update: (state, e) =\u003e {\n        state.push(e)\n      }\n    })\n  }\n\n  onUpdate (state, e, type) {\n    this.history.emit('update', { state, e, type })\n  }\n}\n\nHistoryReducer.prototype['foo/ADD'] = function (state, e, type) {\n  state.foo += e.foo\n  this.onUpdate(state, e, type)\n}\n\nconst historyReducer = new HistoryReducer([])\nstore.handleActions(historyReducer)\n\nstore.emit('foo/ADD', { foo: 5 })\nconsole.log(store.state.foo) // logs 10\nstore.emit('foo/ADD', { foo: 7 })\nconsole.log(store.state.foo) // logs 17\nconsole.log(historyReducer.history.state)\n// logs\n// [\n//   { state: { foo: 10 }, e: { foo: 5 }, type: 'foo/ADD' },\n//   { state: { foo: 17 }, e: { foo: 7 }, type: 'foo/ADD' }\n// ]\n\n```\n\n### Thanks\nThanks to [developit](https://github.com/developit) for [mitt](https://git.io/mitt) and the project structure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkh44%2Fsmitty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkh44%2Fsmitty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkh44%2Fsmitty/lists"}