Simple state management in JavaScript applications.
Create a store and connect it to React components.
Take a look at some simple examples here.
$ npm install single-source
# or yarn add single-source
import { createStore, makeReactConnect } from 'single-source';
// or
const singleSource = require('single-source');
const createStore = singleSource.createStore;
const makeReactConnect = singleSource.makeReactConnect;
import { createStore } from 'single-source';
const initialState = {
items: [],
currentLanguage: 'en',
user: {
email: 'tony@stark.com',
firstName: 'Tony',
lastName: 'Stark'
}
};
const myStore = createStore(initialState);
get the current state
import { createStore } from 'single-source';
const initialState = {
items: [],
currentLanguage: 'en',
user: {
email: 'tony@stark.com',
firstName: 'Tony',
lastName: 'Stark'
}
};
const myStore = createStore(initialState);
myStore.getState();
/*{
items: [],
currentLanguage: 'en',
user: {
email: 'tony@stark.com',
firstName: 'Tony',
lastName: 'Stark'
}
}*/
Get a reduced state based on the given path
import { createStore } from 'single-source';
const USER_EMAIL = 'user.email';
const initialState = {
items: [],
currentLanguage: 'en',
user: {
email: 'tony@stark.com',
firstName: 'Tony',
lastName: 'Stark'
}
};
const myStore = createStore(initialState);
myStore.getState(USER_EMAIL);
// 'tony@stark.com'
changes the state
import { createStore } from 'single-source';
const USER_EMAIL = 'user.email';
const initialState = {
items: [],
currentLanguage: 'en',
user: {
email: 'tony@stark.com',
firstName: 'Tony',
lastName: 'Stark'
}
};
const myStore = createStore(initialState);
myStore.dispatch({
path: USER_EMAIL,
payload: 'ironman@stark.com',
});
myStore.getState(USER_EMAIL);
// 'ironman@stark.com'
If you pass a function as payload it will be executed with the current state (or part of state defiend by "path") as argument.
import { createStore } from 'single-source';
const ITEMS = 'items';
const initialState = {
items: [1, 2, 3, 4],
currentLanguage: 'en',
};
const myStore = createStore(initialState);
const square = currentItemsArray => currentItemsArray.map(n => n * n);
myStore.dispatch({
path: ITEMS,
payload: square,
});
myStore.getState();
/*{
items: [1, 4, 9, 16],
currentLanguage: 'en',
}*/
NOTE: You can not store a function in your state. Just seralizable data can be stored! A function as payload will always executed to recive seralizable data
import { createStore } from 'single-source';
const CURRENT_LANG = 'currentLanguage';
const initialState = {
items: [],
currentLanguage: 'en',
};
const myStore = createStore(initialState);
myStore.subscribe(CURRENT_LANG, (newLanguage) => {
console.log('the new Language is: ', newLanguage);
})
myStore.dispatch({
path: CURRENT_LANG,
payload: 'fr',
});
// log -> 'the new Language is: fr'
NOTE: If .dipatch does not change data the subscribed callback will not be executed.
import { createStore } from 'single-source';
const CURRENT_LANG = 'currentLanguage';
const initialState = {
items: [],
currentLanguage: 'en',
};
const myStore = createStore(initialState);
myStore.subscribe(CURRENT_LANG, (newLanguage) => {
console.log('the new Language is: ', newLanguage);
})
myStore.dispatch({
path: CURRENT_LANG,
payload: 'en',
});
// nothing logged because .dipatch not changed any data
Connect React Components to the store. No Provider Component needed.
import React from 'react';
import ReactDOM from 'react-dom';
import { makeReactConnect, createStore } from 'single-source';
const initialState = {
counter: 0,
};
const store = createStore(initialState);
const COUNTER = 'counter';
const increase = n => (n + 1);
const square = n => (n * n);
const handleIncrease = () => store.dispatch({
path: COUNTER,
payload: increase,
});
const handleSquare = () => store.dispatch({
path: COUNTER,
payload: square,
});
const CounterDisplay = props => (
<input type={'number'} readOnly value={props.counter || 0} />
);
const ConnectedCounterDisplay = makeReactConnect(
React,
store,
{ counter: COUNTER },
)(CounterDisplay);
const App = () => (
<div className="app">
<ConnectedCounterDisplay />
<button onClick={handleIncrease}>+ 1</button>
<button onClick={handleSquare}>n * n</button>
</div>
);
ReactDOM.render(
<App />,
document.getElementById('root'),
);
If you are like me you are already thinking in paths when it comes to serializable data. What i call path does not mean something complicated.
{
user: {
email: 'tony@stark.com',
firstName: 'Tony',
lastName: 'Stark'
}
}
the path 'user.email'
points to 'tony@stark.com'
.
So the string 'user.email'
reduces your data to a specific part of an object.
With this in mind it should be easy to handle bigger states in JavaScript applications. Put paths in constants or create new paths dynamically. Use pure-functions to mutate your state
I hope this small tool helps you decrease the complexity of state management in apps.
If you worked with tools like redux you probably won't replace it with single-source. There are no performance tests for single-source yet.
Thanks for reading!