40
Storecle - a neat app state management for React and Solid
Storecle uses a simple mental model which lets you access app-wide actions and their results by using Context API.
It consists of 4 main building blocks i.e. Store, User Actions (actions triggered by a user), Data Suppliers (actions executed prior to rendering) and Reload Types (action re-trigger groups).
The actions are just functions which are implicitly bound to the Store and write their results by returning/resolving.
Then, their results are accessible by their own names.
It consists of 4 main building blocks i.e. Store, User Actions (actions triggered by a user), Data Suppliers (actions executed prior to rendering) and Reload Types (action re-trigger groups).
The actions are just functions which are implicitly bound to the Store and write their results by returning/resolving.
Then, their results are accessible by their own names.
To improve the code re-usability, Data Suppliers use a middleware pattern. They are executed in the order you specify and pass a snapshot of Store from one to another, letting you split the logic into small, specified functions.
useEffect
/ createEffect
to provide action re-triggers based on specified Store changes.I ❤️ Redux, but it leaves plenty of room to be misused. Hence, Storecle is my proposal to let developers rely less on self-discipline and more on tooling and self-restrictive design.
- No inline: data fetches, transformers, conditionals.
- No nested action dispatchers upon other action completion.
React:
yarn add @gluecodes/storecle-react
or
npm i @gluecodes/storecle-react
Solid:
yarn add @gluecodes/storecle-solid
or
npm i @gluecodes/storecle-solid
This module exports 3 constructs that can be imported for a particular framework in different parts of your app.
import {
builtInActions,
PageProvider,
useAppContext
} from '@gluecodes/storecle-react'
or
import {
builtInActions,
PageProvider,
useAppContext
} from '@gluecodes/storecle-solid'
For the purpose of the example I used a Solid version.
Soon the official starter templates will be released. Using this library means following certain patterns which are explained below using a simple counter example.
See: Code Sandbox example for React.
See: Code Sandbox example for Solid.
File tree:
.
├── actions
│ ├── dataSuppliers (#2)
│ │ └── index.js
│ ├── reloadTypes.js (#4)
│ └── userActions (#3)
│ └── index.js
├── index.jsx (#1)
├── Layout.jsx (#5)
└── partials (#6)
└── Counter
└── index.jsx
Page provider wraps a given Layout around a single app context.
dataSupplierPipeline
- an array providing the order in which Data Suppliers are executed.dataSuppliers
- an object containing Data Suppliers.getLayout
- a function which returns the page Layout.reloadTypes
- an object containing Reload Types.userActions
- an object containing User Actions.onError
- a function triggered when an error is thrown either in Data Suppliers or User Actions../index.jsx
import { PageProvider } from '@gluecodes/storecle-solid'
import * as dataSuppliers from './actions/dataSuppliers/index'
import * as userActions from './actions/userActions/index'
import * as reloadTypes from './actions/reloadTypes'
import Layout from './Layout.jsx'
export default () => (
<PageProvider
dataSupplierPipeline={[
dataSuppliers.getTexts,
dataSuppliers.getCounter
]}
dataSuppliers={dataSuppliers}
getLayout={() => Layout}
reloadTypes={reloadTypes}
userActions={userActions}
onError={(err) => {
console.error(err)
}}
/>
)
Data suppliers provide data prior to rendering. Note the early returns which demonstrate how to resolve cached data based on Reload Type.
buildInActions
- an object containing the following built-in User Actions:
-
onStoreChanged
- a function which receives a callback to be triggered when Store changes. -
runUserActions
- a function which allows for executing multiple User Actions at once. -
runDataSuppliers
- a function which receives a Reload Type name. Note that it's exposed to ease the integration with legacy apps. Don't call it manually as Data Suppliers are implicitly reloaded based on the provided Reload Types.
resultOf
and nameOf
.
-
resultOf
- a function providing a result of a given Data Supplier or User Action. -
nameOf
- a function providing a name of either Data Supplier, User Action or Reload Type.
./actions/dataSuppliers/index.js
import { builtInActions } from '@gluecodes/storecle-solid'
import { reFetchCounter } from '../reloadTypes'
export function getCounter (resultOf, nameOf) {
const reloadType = resultOf(builtInActions.runDataSuppliers)
const shouldFetch =
reloadType === 'full' || reloadType === nameOf(reFetchCounter)
if (!shouldFetch) {
return resultOf(getCounter)
}
return global.sessionStorage.getItem('appWideCounter') || 0
}
export function getTexts (resultOf) {
if (resultOf(builtInActions.runDataSuppliers) !== 'full') {
return resultOf(getTexts)
}
return {
Click: 'Click'
}
}
Actions triggered by a user.
./actions/userActions/index.js
export function incrementCounter (counter) {
const incrementedCounter = Number(counter) + 1
global.sessionStorage.setItem('appWideCounter', incrementedCounter)
}
A way to tell the app to re-run Data Suppliers based on executed User Actions.
runDataSuppliers
and reloads all Data Suppliers. nameOf
and returns an array of User Action names.
-
nameOf
- a function providing a name of User Action.
./actions/reloadTypes.js
import { incrementCounter } from './userActions/index'
export const reFetchCounter = (nameOf) => [
nameOf(incrementCounter)
]
Nothing else than the page layout.
./Layout.jsx
import Counter from './partials/Counter/index.jsx'
export default () => (
<div className='container'>
<Counter />
</div>
)
Partials are self-contained pieces of UI which have access to app state via the app context.
useAppContext
- a function which returns an array of 3 items: resultOf
, action
, nameOf
.
-
resultOf
- a function providing a result of a given Data Supplier or User Action. -
action
- a function which triggers User Action. -
nameOf
- a function providing a name of either Data Supplier or User Action.
./partials/Counter/index.jsx
import { useAppContext } from '@gluecodes/storecle-solid'
import { getCounter, getTexts } from '../../actions/dataSuppliers/index'
import { incrementCounter } from '../../actions/userActions/index'
export default () => {
const [resultOf, action] = useAppContext()
return (
<button
onClick={() => {
action(incrementCounter)(
resultOf(getCounter)
)
}}
>{resultOf(getTexts)?.Click}: {resultOf(getCounter)}</button>
)
}
Here is the open source Github repo. Feel free to suggest your ideas either in comments or in the repo issues. If you like it, a star would be appreciated 😉
40