react-xs
v1.0.33
Published
Minimalism state manager
Downloads
71
Maintainers
Readme
react-xs
Minimalism state manager
Features
| | react-xs | redux | | ------------------------- | :------: | :---: | | Provider element | | ✓ | | Action Dispatcher | | ✓ | | Action Creator | | ✓ | | Reducer | | ✓ | | Middleware | | ✓ | | connect() HOC | | ✓ | | State Mappings / Bindings | | ✓ |
react-xs has nothing special but it is powerful, easy to use, reduce code complexity and great performance
Table of contents
- Counter App
- Todo App
- Creating simple state
- Handling state change
- Getting state value
- Sub States
- Binding states to component
- Mutating multiple states and performance issue
- Mutating state using helpers
- Handling multiple states change
- Computed state
- Getting values of multiple states
- Update values of multiple states
- Dispatching function one time when component did mount
- Best practice
- API
- Credits
Counter App
Simplest and shortest counter app
import React from "react";
import { render } from "react-dom";
import $ from "react-xs";
const count = $(1);
const Counter = $(() => (
<>
<h1>{count.value}</h1>
<button onClick={() => count.value++}>Increase</button>
</>
));
render(<Counter />, document.getElementById("root"));
Compare to redux version
import React from "react";
import { render } from "react-dom";
import { createStore } from "redux";
import { Provider, connect } from "react-redux";
const reducer = (state = 0, action) => {
switch (action.type) {
case "INCREMENT":
return state + 1;
default:
return state;
}
};
const store = createStore(reducer);
const App = connect(state => ({ count: state }))(({ dispatch, count }) => (
<>
<h1>{count}</h1>
<button onClick={() => dispatch({ type: "INCREMENT" })}>Increase</button>
</>
));
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Redux like version
import React from "react";
import { render } from "react-dom";
import $ from "react-xs";
const connect = $;
const Count = $(1);
const Increase = () => Count.value++;
const Counter = connect(
{
// map state to props
states: { count: Count },
// dispatch to props
actions: {
increase: Increase
}
},
({ count, increase }) => (
<>
<h1>{count}</h1>
<button onClick={increase}>Increase</button>
</>
)
);
render(<Counter />, document.getElementById("root"));
Todo App (Performance test)
Please refer this link , an example runs with 7000 todo items (over 28.000 elements). User can update todo text on the fly, without lagging. Redux or other state managers(Mobx, unstated) get lag with 1000-5000 todo items
Creating simple state
import $ from "react-xs";
const numberValue = $(100);
const booleanValue = $(true);
// mutate states
numberValue.value = 1000;
booleanValue.value = false;
// keep in mind, state can be anything, no validation or restriction for state data type
numberValue.value = new Date();
Handling state change
import $ from "react-xs";
const state = $(1);
state.subscribe(nextValue => console.log(nextValue));
state.value = 100;
Getting state value
import $ from "react-xs";
const person = $({ name: "Hung", address: { street: "abc", city: "def" } });
person.value.name; // Hung
person.get("name"); // Hung
person.get`name`; // Hung
person.get("address.street"); // abc
person.value.address.street; // abc
person.get`address.street`; // abc
Sub States
const person = $({ name: "Hung", address: { street: "abc", city: "def" } });
person.prop`address.street`.value; // abc
person.prop(`address.street`).value; // abc
Binding states to component
You can use overload $(component) to create component wrapper which binded with states. You dont need any Provider element, no mapStateToProps needed
import React from "react";
import $ from "react-xs";
const state = $(1);
const WrappedComponent = $(props => {
return <div>{state.value}</div>;
});
Mutating multiple states and performance issue
Sometimes you need to mutate many states at same time, that might lead to performance issue because when state changed, it notifies to all components which is using state
import React from "react";
import $ from "react-xs";
const state1 = $(1);
const state2 = $(2);
const Comp1 = $(() => <div>{state1.value}</div>);
const Comp2 = $(() => (
<div>
{state1.value}
{state2.value}
</div>
));
function DoSomething() {
state1.value = 100; // at this time, both Comp1 and Comp2 re-render
state2.value = 100; // Only Comp2 re-renders
}
We can use $.mutate(action) to reduce component re-render times
function DoSomething() {
$.mutate(() => {
state1.value = 100;
state2.value = 100;
});
// Comp1 and Comp2 re-render one time only
}
Mutating state using helpers
react-xs provides many helpers for mutating object, array, date, number, boolean, string data types
import $ from "react-xs";
const number = $(0);
number.add(1); // 1
number.add(-2); // -1
number.mul(2); // -4
number.div(2); // -2
const obj = $({});
obj.set("name", "Hung"); // { name: 'Hung' }
obj.toggle("male"); // { name: 'Hung', male: true }
obj.prop`parent.address.street`.value = "abc"; // { name: 'Hung', male: true, parent: { address: { street: 'abc' } } }
obj.unset("parent", "male"); // { name: 'Hung' }
// set default value for prop if it is not present
obj.def("male", false); // { name: 'Hung', male: false }
// nothing affected because male prop is present
obj.def("male", true); // { name: 'Hung', male: false }
const array = $([]);
array.push(1, 2, 3); // [1, 2, 3]
array.splice(1, 1); // [1, 3]
array.map(x => x * 2); // [2, 6]
array.pop(); // [2]
array.shift(); // []
array.unshift(1, 2); // [1, 2]
array.filter(x => x % 2 === 0); // [2]
array.push(5, 6, 2); // [2, 5, 6, 2]
array.exclude(2); // [5, 6]
array.remove(1); // [5]
array.fill(100); // [100]
array.push(99); // [100, 99]
array.first() === 100; // true
array.last() === 99; // true
array.sort(); // [99, 100]
const date = $(new Date("2019-01-01"));
date.add(1, "Y"); // add 1 year, 2020-01-01
date.add(1, "year"); // add 1 year, 2021-01-01
// duration can be year/Y, month/M, day/D,
// hour/h, minute/m, second/s, milli
// you also add multiple durations
date.add([1, "M"], [2, "D"]); // add 1 month and 2 days, 2021-02-03
If you want to extend more helpers, just call $.extend()
import $ from "react-xs";
import immhelper from "immhelper";
$.extend({
update(specs) {
return this.mutate(current => immhelper(current, specs));
}
});
const state = $({
name: "N/A"
});
state.update({
name: ["set", "Hung"]
});
// { name: 'Hung' }
Handling multiple states change
import $ from "react-xs";
const state1 = $(0);
const state2 = $(0);
$.subscribe([state1, state2], () => {
console.log("test1", state1.value, state2.value);
});
// using debounce option
$.subscribe(
[state1, state2],
(state1Value, state2Value) => {
console.log("test2", state1Value, state2Value);
},
{
debounce: 100
}
);
state1.value = 1;
state2.value = 2;
// output
// test1 1 0
// test1 1 2
// test2 1 2
Computed state
import $ from "react-xs";
const state1 = $(1);
const state2 = $(2);
const state3 = $().compute([state1, state2], () => state1.value + state2.value); // state3 = 3
state1.value = 100; // state3 = 102
state2.value = 101; // state3 = 201
Getting values of multiple states
import $ from "react-xs";
const state1 = $(1);
const state2 = $(2);
$.get({
state1,
state2
}); // { state1: 1, state2: 2 }
Update values of multiple states
import $ from "react-xs";
const state1 = $(1);
const state2 = $(2);
const state3 = $(3);
$.set(
{
state1,
state2
},
{
state1: 5,
state2: 6,
state3: 7
}
);
// state1 = 5
// state2 = 6
// state3 = 3
Dispatching function one time when component did mount
import React from "react";
import { render } from "react-dom";
import $ from "react-xs";
// assign default async status to state value
// initial { loading:false, done:false, data:undefined, error:undefined }
// loading { loading:true, done:false, data:undefined, error:undefined }
// resolved { loading:false, done:true, data:any, error:undefined }
// rejected { loading:false, done:true, data:undefined, error:any }
const userProfile = $().async();
const LoadUserProfile = () => {
// do nothing if we already fetched data
if (userProfile.get`done`) {
return;
}
// update state once promise resolved/rejected
userProfile.async(
fetch("https://demo9029075.mockable.io/react-xs-user-profile").then(res =>
res.json()
)
);
};
const UserProfileComponent = $(() => {
// dispatch LoadUserProfile once when component did mount
$.one(LoadUserProfile);
// render userProfile according to its states
return userProfile.async({
loading: "Loading...",
success: data => JSON.stringify(data)
});
});
render(<UserProfileComponent />, document.getElementById("root"));
Best practice
Sometimes we try to split our app to many modules, it is hard for other state management libraries. Here is sample project structure if you are using react-xs
modules/counter/states.js
import $ from "react-xs";
export const CountState = $(0);
modules/counter/actions.js
import { CountState } from "./state.js";
export function Increase() {
CountState.value++;
}
export function Decrease() {
CountState.value--;
}
export async function Load() {
const payload = await fetch(
"https://demo9029075.mockable.io/react-xs-counter"
).then(res => res.json());
CountState.value = payload.count;
}
// auto increase count each 3s
setInterval(Increase, 3000);
modules/counter/components/container.js
import { CountState } from "../state.js";
import { Increase, Decrease, Load } from "../actions.js";
import $ from "react-xs";
export default $.hoc(props => {
return {
...props,
count: CountState.value,
increase: Increase,
decrease: Decrease,
load: Load
};
});
modules/counter/components/index.js
import React from "react";
import container from "./container";
export default container(({ count, increase, decrease, load }) => {
return (
<>
<h1>{count}</h1>
<button onClick={increase}>Increase</button>
<button onClick={decrease}>Decrease</button>
<button onClick={load}>Load</button>
</>
);
});
Working with class component
You can use react-xs with any component types, even class component. That helps you bring react-xs power to old components and refactor them all to functional components later
import React, { Component } from "react";
import { render } from "react-dom";
import $ from "react-xs";
const Count = $(0);
const Increase = () => Count.value++;
class Counter extends Component {
_ = $.bind(this);
render = () => (
<>
<h1>{Count.value}</h1>
<button onClick={Increase}>Increase</button>
</>
);
}
render(<Counter />, document.getElementById("root"));