repostream
v1.3.4
Published
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
Downloads
20
Readme
RepoStream
Concept
RepoStream serves as an intermediary layer between the UI and Business logic of your application. It leverages the StateStream
to deliver the application state to the UI, while incorporating business logic through the use of middlewares
and joinStates
within the RepoStream
.
RepoStream
RepoStream acts as the central manager of the application states. Clients connect to one or multiple states within the repo by utilizing the createStream
method, which returns a StateStream
object connected to the desired states. Communication between the repo and streams occurs via event messages. Clients can employ three different messages (push
, pop
, and request
) to instruct the repo to modify the application state.
State and State Path
RepoStream maintains all application states within its states
object. Here's an example of the states object structure:
states = {
stores: {
city: {
hcmc: [
'Dev Store', 'Play Store'
]
}
},
stocks: {
games: [
{
title: 'Awesome adventure of a poor dev',
quantity: 100
}
]
}
}
In the above example, two application states are present: stores
and stocks
. Each state acts as the root of its respective state tree. For instance, stores
and stocks
serve as the root for their corresponding state trees. A state path represents the path to reach a specific point within the state tree. For example, stocks.games
and stores.city.hcmc
are state paths within the provided example.
State path support wildcast * character, for example 'users.*.profile' will match users.bob.profile and users.anna.profile.
The StateStream object can request changes to the state root or a specific state path. You need to define the middleware function to handle the request for a specific state path
Middleware
const middleware = {
push: (changePath, changeValue, currentValue, state, setState, setError) => {
currentValue.push(changeValue);
return currentValue;
}
}
const repo = new Repo();
repo.addMiddleware('stores.city.*', middleware);
stream = repo.createStream(['stocks', 'stores']);
stream.push('stores.city.hcmc', 'Game Store');
The middleware function handles state path requests and can return a new state value for the specified changePath. Alternatively, it can return a promise that resolves to a new value.
If the middleware function returns undefined, the state will not be updated unless setState is called within the middleware body.
To manipulate the state object, you can use the setState callback. In certain cases, such as deleting a state branch, the callback is useful. It accepts a new state tree or a function to avoid potential issues with stale state.
Here's an example of defining a middleware function:
const middleware = {
remove: (changePath, changeValue, currentValue, state, setState, setError) => {
delete state.stocks.games;
setState(state);
},
}
joinState
The joinState
method in the RepoStream API allows you to create a virtual state by joining multiple states together. Whenever any of the states in the joining array changes, the joined state will be automatically updated using the provided joinFunction
.
Usage
repo.joinState(name, states, joinFunction);
name
(string): The name of the joining state.states
(array): An array of states to join together.joinFunction
(function): A function that accepts the repo's state object, performs logic to join the states, and returns the joined state.
The joinFunction
takes the repo's state object as an argument and should return the joined state object.
Example
Here's an example of using joinState
to create a virtual state called itemsInStore
by joining the stocks
and stores
states:
repo.joinState('itemsInStore', ['stocks', 'stores'], state => {
const itemsInStore = {};
for (let store of state.stores) {
const items = [];
for (let item in state.stocks) {
items.push({
name: item,
quantity: state.stocks[item].filter(item => item.store === store).length
});
}
itemsInStore[store] = items;
}
return itemsInStore;
});
In the above example, the joinFunction
iterates over each store in the stores
state and collects the items from the stocks
state that belong to that store. It creates an object itemsInStore
where each store is a key and its corresponding value is an array of items with their quantities. Whenever either the stocks
or stores
state changes, the itemsInStore
state will be updated accordingly.
The joined state itemsInStore
can then be accessed and used in the application to display the relevant information.
Installation
To use RepoStream in your project, follow these steps:
- Install the package via npm:
npm install repostream --save
- Import RepoStream into your project:
import RepoStream from 'repostream';
Usage
Here's an example of how to use RepoStream:
import RepoStream from 'repostream';
const schemas = {
profile: {
username: 'username',
picture: 'default_url'
},
enrolls: [],
score: {
math: 0,
physics: 0,
}
};
const repo = new RepoStream({ schemas });
repo.addMiddleware('user.enrolls', {
push: (changePath, changeValue, currentValue, state, setState, setError) => {
state.user.enrolls = [...state.user.enrolls, changeData];
setState(state);
}
});
// Create a stream that connects to the 'user' state in the repo
const stream = repo.createStream(['user']);
// Set up listeners for state changes and errors
stream.onStateChange(state => console.log(state));
stream.onError(err => console.error(err));
// Push a new state change to the stream
const requestPush = stream.request('push');
requestPush('user.enrolls', 'javascript');
console.log(stream.state);
// Remove the stream itself
stream.remove();
Developer Document
RepoStream API
Methods
constructor({ schemas })
: Initializes a new instance of RepoStream with the specified schemas.createStream(states)
: Creates a stream that connects to the specified states in the repo.addMiddleware(statePath, middleware)
: Adds middleware for the given state path.invokeMiddleware({ statePath, action, changeValue })
: Invode the middleware.joinState(name, states, joinFunction)
: Joins multiple states using a custom join function.
Properties
schemas
: The schemas defined for RepoStream.repo
: The underlying Repo instance.streams
: The active streams created from the repo.
StateStream API
Methods
constructor({ id, eventEmitter, state })
: Initializes a new instance of StateStream with the provided parameters.request(requestType)
: Returns a requester function that can be used to request a state change.onStateChange(callback)
: Sets up a listener for state change events.onError(callback)
: Sets up a listener for error events.remove()
: Removes the StateStream.
Properties
state
: The current state associated with the StateStream.
EventEmitter API
Methods
addListener(eventName, handler)
: Adds a listener for the specified event.removeListener(eventName, handler)
: Removes a previously added event listener.emit(eventName, ...args)
: Emits an event with optional arguments.
TODO
- Create
repostream-middlewares
package that includes common middleware functions, such as pushing to an array. - Enable the usage of
repo.addMiddleware('state.path', { push: mw.pushToArray })
.
Contributing
Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request.
License
This project is licensed under the MIT License.