Store is a lightweight shared state library by theStencilJScore team. It implements a simple key/value map that efficiently re-renders components when necessary.
Highlights:
- Lightweight
- Zero dependencies
- Simple API, like a reactive Map
- Best performance
npm install @stencil/store --save-dev
store.ts:
import{createStore}from"@stencil/store";
const{state,onChange}=createStore({
clicks:0,
seconds:0,
squaredClicks:0
});
onChange('clicks',value=>{
state.squaredClicks=value**2;
});
exportdefaultstate;
component.tsx:
import{Component,h}from'@stencil/core';
importstatefrom'../store';
@Component({
tag:'app-profile',
})
exportclassAppProfile{
componentWillLoad(){
setInterval(()=>state.seconds++,1000);
}
render(){
return(
<div>
<p>
<MyGlobalCounter/>
<p>
Seconds:{state.seconds}
<br/>
Squared Clicks:{state.squaredClicks}
</p>
</p>
</div>
);
}
}
constMyGlobalCounter=()=>{
return(
<buttononClick={()=>state.clicks++}>
{state.clicks}
</button>
);
};
Create a new store with the given initial state. The type is inferred frominitialState
,or can be passed as the generic typeT
.
initialState
can be a function that returns the actual initial state. This feature is just in case you have deep objects that mutate
as otherwise we cannot keep track of those.
const{reset,state}=createStore(()=>({
pageA:{
count:1
},
pageB:{
count:1
}
}));
state.pageA.count=2;
state.pageB.count=3;
reset();
state.pageA.count;// 1
state.pageB.count;// 1
Please, bear in mind that the object needs to be created inside the function, not just referenced. The following example won't work as you might want it to, as the returned object is always the same one.
constobject={
pageA:{
count:1
},
pageB:{
count:1
}
};
const{reset,state}=createStore(()=>object);
state.pageA.count=2;
state.pageB.count=3;
reset();
state.pageA.count;// 2
state.pageB.count;// 3
By default, store performs a exact comparison (===
) between the new value, and the previous one in order to prevent unnecessary rerenders, however, this behaviour can be changed by providing ashouldUpdate
function through the second argument. When this function returnsfalse
,the value won't be updated. By providing a customshouldUpdate()
function, applications can create their own fine-grained change detection logic, beyond the default===
.This may be useful for certain use-cases to avoid any expensive re-rendering.
constshouldUpdate=(newValue,oldValue,propChanged)=>{
returnJSON.stringify(newValue)!==JSON.stringify(oldValue);
}
Returns astore
object with the following properties.
The state object is proxied, i. e. you can directly get and set properties. If you access the state object in therender
function of your component, Store will automatically re-render it when the state object is changed.
Note:Proxy
objects are not supported by IE11 (not even with a polyfill), so you need to use thestore.get
andstore.set
methods of the API if you wish to support IE11.
Add a listener to the store for a certain action.
Add a listener that is called when a specific property changes (either from aset
orreset
).
Get a property's value from the store.
Set a property's value in the store.
Reset the store to its initial state.
Use the given subscriptions in the store. A subscription is an object that defines one or more of the propertiesget
,set
orreset
.
const{reset,state,use}=createStore({a:1,b:2});
constunlog=use({
get:(key)=>{
console.log(`Someone's reading prop${key}`);
},
set:(key,newValue,oldValue)=>{
console.log(`Prop${key}changed from${oldValue}to${newValue}`);
},
reset:()=>{
console.log('Store got reset');
},
dispose:()=>{
console.log('Store got disposed');
},
})
state.a;// Someone's reading prop a
state.b=3;// Prop b changed from 2 to 3
reset();// Store got reset
unlog();
state.a;// Nothing is logged
state.b=5;// Nothing is logged
reset();// Nothing is logged
Resets the store and all the internal state of the store that should not survive between tests.
Like any global state library, state should bedispose
d between each spec test.
Use thedispose()
API in thebeforeEach
hook.
importstorefrom'../store';
beforeEach(()=>{
store.dispose();
});