A package for easily creating and managing a central store in svelte for occasions where data is loaded asynchronously. The package manages both the data and the load state, and is ideal for cases where the speed and reliability is unpredictable.
Your data store must be initialised by specifying the names and types of each property. This is done with the initData
function. This function takes a single parameter, dataStructure which is an object specifying each property's type, and an optional
default value.
function initData( dataStructure )
dataStructure takes the following form:
const dataStructure = {
//propety without specified initial value
propertyName: [propertyType],
//or propety with specified initial value
propertyName: [propertyType, initialValue],
}
Every type has a default initial value if you don't otherwise specify it. The following is a list of possible types and their initial values:
| Type | propertyType |
Default initial value |
|---|---|---|
| Number | "number" |
0 |
| BigInt | "bigInt" |
0n |
| Boolean | "boolean" |
false |
| Array | "array" |
[] |
| Object | "object" |
{} |
resetDataYou can reset the data to its initialised state at any time with resetData. This will set all properties to their
initial values, with the load state set to initial.
$data and $valuesThe central store can be read a number of ways, but it provides two stores, $data, and the derived store $values.
Their exact structure depends on how you initialise them, but each property corresponds to a property
in the dataStructure you initialised them with.
$values$values is just an object with the current values, but contains no information about load state. So if you have
initialised your data store but not loaded any data, all the properties of $values will exist, but just be their
initial values. These properties will change in the event that $data is updated.
A variable _values, is also exported, which is just the object in $values, but not as a store.
$dataEach property of $data corresponds to a property in the dataStructure object you used to initialise the package.
However, each property is a Loadable object. This means it takes the following structure:
{
// the current value
value: current_value,
// the property's type as defined in `dataStructure`
type: type,
// the current load-state of the property
state: "initial" | "loading" | "loaded" | "error",
// four aliases, only one of them will be true at any given time, if the current load-state matches that property
initial: true | false,
loading: true | false,
loaded: true | false,
error: true | false,
}
//Note: the load-states are automatically managed by other functions of the package, not manually.
As indicated above, every property in $data has an associated load state. The load state of each property is
automatically managed by other functions of the package.
The four load states are:
initialThe initial, default state of a property. This means that it has its initial value.
loadingA load function (loadData or loadDatas) is in the process of attempting to retrieve data for this property.
loadedA load function has successfully been used to retrieve data for this property.
errorAn error occurred while using a load function to retrieve data for this property.
There are two load functions which are used for externally loading data to update your data store. loadData for when
you are only loading a single property from an external call, or loadDatas if the external call will retreive
multiple properties at once.
loadDataloadData(propertyName, async ()=>{
// function that returns the new property
})
To use loadData the first param is the property you want to load, the second is a function that returns the new value.
When called, this will flag the property as loading, and will then update to either loaded (with the new value set)
if the load function is successful, or error if it throws for some reason.
example:
// Load the value of someString
await loadData("someString", async()=>{
//someString will now be flagged as "loading"
//do some async stuff to fetch the value from elsewhere
const value = await some.async.thing();
// return it, and it will update someString, and flag it as loaded
return value;
});
loadDatasloadDatas([...propertyNames], async ()=>{
// function that returns the new properties
})
To use loadDatas, the first param is an array of names of the properties you want to load, the second is a function
that returns the new values as an object (each key is the corresponding property). When called, this will flag all the
listed properties as loading, and will then update them to either loaded (with new value set) if the load function
is successful, or error if it throws for some reason.
example:
// Load the value of someString, someOtherString and someNumber
await loadDatas(["someString","someOtherString", "someNumber"], async()=>{
//someString, someOtherString and someNumber will now be flagged as "loading"
//do some async stuff to fetch the values from elsewhere
const values = await some.async.thing();
// return them, and it will update the properties, and flag them as loaded
return {
someString: values.something,
someOtherString: values.somethingElse,
someNumber: values.somethingNumerical,
};
});
getDataYou can also retrieve the value of any property with:
getData(propertyName)
readObjectIn special cases where the property is an object, you can use
readObject(propertyName,objectPropertyName)
hasPropertyIn special cases where the property is an object, you can check if it has a sub-property with
hasProperty(propertyName,objectPropertyName)
isLoadedisLoaded(propertyName)
Returns true if the property is loaded.
There are a number of ways to update data so that things change properly within their desired lifecycles. There are a
number of "safe" functions which will do the desired action if the property is loaded, but do nothing (ie, not
throw but not make any changes) if the state is otherwise.
safeUpdatesafeUpdate(propertyName, newValue);
Just set the new value
safeIncrement and safeDecrementsafeIncrement(propertyName);
safeDecrement(propertyName);
For numerical types (number and bigInt), increments or decrements the value by 1.
safePush and safeRemovesafePush(propertyName, value);
safeRemove(propertyName, value);
For arrays, push an item, or find and remove an item by value.
safeSetObjProp and safeDeleteObjPropsafeSetObjProp(propertyName, objectPropertyName, value);
If a property of your data store is an object, this can be used to set the value of one of it's sub-properties.
safeDeleteObjProp(propertyName, objectPropertyName);
If a property of your data store is an object, this can be used to delete one of it's sub-properties.
setDataThe setData function can be used to set the value of a property without regard for whether it is loaded.
setData(propertyName,value, _stateId = stateId, updateWritable = true)
Note:
_stateId is an internally tracked value that ensures slow asynchronous calls do not interfere with the data store in
the event that it is re-initialised.
updateWritable is a bool, if false it will not update the store itself, just _data.
Caution: you probably don't wanna do this unless you really know what you're doing.
Forcefully set the load state of a property with:
setInitial(propertyName)
setLoading(propertyName)
setLoaded(propertyName)
setError(propertyName)
This just changes their state flags and not the values.
// Initialise the Data Store by specifying the strcture, the types (and optional initial value)
const structur= {
someString: ["string"],
someStringWithInitialValue: ["string","initial value of string"],
someNumber: ["number"],
someBigInt: ["bigInt"],
someBoolean: ["boolean"],
someArray: ["array"],
someObject: ["object"],
}
initData(structure);
// Load the value of just someString
await loadData("someString", async()=>{
const value = await some.async.thing();
return value;
});
// Load the values of both someNumber and someBigInt at the same time.
await loadDatas(["someNumber","someBigInt"], async()=>{
const {someNumber, someBigInt} = await some.async.thing2();
return {
someNumber,
someBigInt
}
});
// Listen to events and trigger updates based on that
somethingWithEventListeners.on("someEvent", (newBoolean, newBigInt)=>{
// Set new values
safeUpdate("soemeBoolean", newBoolean);
safeUpdate("someBigInt", newBigInt);
//Increment someNumber
safeIncrement("someNumber");
});