https://github.com/geotrev/core-flux
½kb functional flux utility. Control the flow of state data between subscribers.
https://github.com/geotrev/core-flux
architecture bindings dispatch flux payload reducer redux state state-management subscription
Last synced: 9 months ago
JSON representation
½kb functional flux utility. Control the flow of state data between subscribers.
- Host: GitHub
- URL: https://github.com/geotrev/core-flux
- Owner: geotrev
- Created: 2021-04-22T06:39:20.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2025-08-31T11:05:25.000Z (9 months ago)
- Last Synced: 2025-08-31T13:06:46.362Z (9 months ago)
- Topics: architecture, bindings, dispatch, flux, payload, reducer, redux, state, state-management, subscription
- Language: JavaScript
- Homepage:
- Size: 899 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
Core Flux
½kb functional flux utility. Control the flow of state data between subscribers.
[See a demo of Core Flux in action!](https://todo-rotom.netlify.app/)
- [Install](#install)
- [API](#api)
- [`createStore()`](#createstore)
- [`subscribe()`](#subscribe)
- [`dispatch()`](#dispatch)
- [Exposing the store](#exposing-the-store)
- [Data model](#data-model)
- [Data flow](#data-flow)
## Install
### NPM / Yarn
```sh
$ npm i core-flux
```
```sh
$ yarn i core-flux
```
### CDN
The CDN puts the library on `window.CoreFlux`.
```html
```
## API
createStore(initialSate, reducer, bindSubscriber, bindState)
The one and only export of Core Flux. Use it to create a store instance. You can create as few or as many stores as your heart desires! They will all be independent from one another.
_**NOTE**: The base state type must be a plain object. Invalid types will throw an error._
The function **requires** all four of its arguments, including [bindings](#bindings):
```js
// foo-store.js
import { createStore } from "core-flux"
import { reducer, bindSubscriber, bindState } from "./foo-bindings"
const initialState = {
foo: [],
bar: { baz: 0, beep: "hello" },
}
const { subscribe, dispatch } = createStore(
initialState,
reducer,
bindSubscriber,
bindState
)
export { subscribe, dispatch }
```
Once a store is created, you'll be able to add subscriptions with `subscribe` and request state updates with `dispatch`.
subscribe(subscriber, data)
Adds a subscription to your store. It will always be tied to a single store, and subsequently state object.
```js
import { subscribe } from "./foo-store"
class FooItems {
constructor() {
subscribe(this, ["foo"])
}
get items() {
return this.foo
}
}
```
In the above example, we've designed the subscriber, the `FooItems` class, to declare an array of strings correlating to properties in the store's state. If you're from the Redux world, this is akin to "connecting" a consumer to a provider via higher-order function/component.
After the subscribe call is made, your `bindSubscriber` function will be called where you can pass along the default values as you see fit.
_**NOTE:** In general, you should try to use a simple data structure as the second argument to `subscribe`; this ensures your bindings have generic and consistent expectations._
dispatch(type, payload)
Requests a state change in your store.
We can extend the previous example with a setter to call `dispatch`:
```js
import { subscribe, dispatch } from "./foo-store"
class FooItems {
constructor() {
subscribe(this, ["foo"])
}
get items() {
return this.foo
}
addItem(item) {
dispatch("ADD_ITEM", { item })
}
}
const fooBar = new FooItems()
fooBar.addItem("bop")
```
Now when the `addItem` method is called, Core Flux will pass along the action type and payload to your reducer.
The reducer could have a logic branch on the action type called `ADD_ITEM` which adds the given item to state, then returns the resulting new state (containing the full items list).
Finally, the result would then be handed over to your `bindState` binding.
_**NOTE:** Much like in `subscribe`, it's best to maintain data types in the payload so your reducer can have consistent expectations._
#### Bindings
Here's a breakdown of each binding needed when initializing a new store:
##### **`bindSubscriber(subscription, state)`**
> `subscription ([subscriber, data])`: A tuple containing the subscribed object and its state-relational data.
`state (object)`: The current state object.
Called after a new `subscribe` is made and a subscription has been added to the store. Use it to _set initial state_ on the new subscriber. Use the `data` provided to infer a new operation, e.g., setting a stateful property to the subscriber.
##### **`reducer(state, action)`**
> `state (object)`: Snapshot of the current state object.
`action ({ type: string, payload: object })`: The dispatched action type and its payload.
Called during a new `dispatch`. Create a new version of state and return it.
##### **`bindState(subscriptions, reducedState, setState)`**
> `subscriptions (subscription[])`: An array containing all subscriptions.
`reducedState (object)`: The state object as returned by the reducer.
`setState (function)`:
Called at the end of a `dispatch` call, after your reducer callback has processed the next state value. Set your new state back to subscribers **and** back to the store. It's possible and expected for you to call `bindSubscriber` again to DRYly apply these updates. You can return from this function safely to noop.
## Exposing the store
For utility or debugging reasons, you may want to look at the store you're working with. To do so, you can use the `__data` property when creating a store:
```js
const fooStore = createStore(initialState, reducer, bindSubscriber, bindState)
window.fooStoreData = fooStore.__data
console.log(window.fooStoreData) // { state: {...}, subscriptions: [...] }
```
_**NOTE:** Avoid over-referencing or depending on `__data` too deeply. The data is mutable and changing it directly will cause unexpected behavior._
## Data model
Core Flux has a relatively simple data model that you should understand when creating [bindings](#bindings).
Here is how state looks in all cases:
```js
Store {
state: { ... },
subscriptions: [
[subscriber, data],
[subscriber, data],
[subscriber, data],
// ...
]
}
```
Each item in `subscriptions` contains a `subscriber` and some form of `data` that informs a relationship between `state` and `subscriber`.
_**NOTE:** \_You_ define `data` in the above model. This ensures that ultimately you control communicating state relationships to subscribers.\_
## Data flow
Here is the general lifecycle of subscribing to the store & dispatching a state update.
- `subscribe` > `bindSubscriber`
- `dispatch` > `reducer` > `bindState`