Skip to content

Commit

Permalink
Convert onAfterUpdate to be more easy, tiny and powerful (#22)
Browse files Browse the repository at this point in the history
* Convert onAfterUpdate to be more easy, tiny and powerful

* Move prevStore declaration to avoid memory problems

* Update eslint rule

* Add test

* Update tests/onAfterUpdate.test.js
  • Loading branch information
aralroca authored Nov 5, 2021
1 parent 8ba1f0a commit 8f05b6d
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 129 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
'require-jsdoc': 0,
'react/prop-types': 0,
'prefer-const': 0,
'linebreak-style': ['error', process.platform === 'win32' ? 'windows' : 'unix'],
'max-len': [
'error',
{'code': 80, 'ignoreStrings': true},
Expand Down
85 changes: 42 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const initialStore = {
cart: { price: 0, items: [] },
};

function onAfterUpdate({ path, value, prevValue })
function onAfterUpdate({ store, prevStore })
console.log("This callback is executed after an update");
}

Expand Down Expand Up @@ -212,10 +212,10 @@ function Example() {

_Input:_

| name | type | description | example |
| --------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Initial value | `any` | This parameter is **not mandatory**. It only makes sense for new store properties that have not been defined before within the `createStore`. If the value has already been initialized inside the `createStore` this parameter has no effect. | `const [price, setPrice] = useStore.cart.price(0)` |
| event after an update | `function` | This parameter is **not mandatory**. Adds an event that is executed every time there is a change inside the indicated store portion. | `const [price, setPrice] = useStore.cart.price(0, onAfterUpdate)`<div><small>And the function:</small></div><div>`function onAfterUpdate({ path, value, prevValue }){ console.log({ prevValue, value }) }`</div> |
| name | type | description | example |
| --------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Initial value | `any` | This parameter is **not mandatory**. It only makes sense for new store properties that have not been defined before within the `createStore`. If the value has already been initialized inside the `createStore` this parameter has no effect. | `const [price, setPrice] = useStore.cart.price(0)` |
| event after an update | `function` | This parameter is **not mandatory**. Adds an event that is executed every time there is a change inside the indicated store portion. | `const [price, setPrice] = useStore.cart.price(0, onAfterUpdate)`<div><small>And the function:</small></div><div>`function onAfterUpdate({ store, prevStore }){ console.log({ store, prevStore }) }`</div> |

_Output:_

Expand All @@ -235,25 +235,21 @@ It works exactly like `useStore` but with **some differences**:
- It's **not possible to register events** that are executed after a change.

```js
getStore.cart.price(0, onAfterPriceChange) //
getStore.cart.price(0, onAfterPriceChange); //

function onAfterPriceChange({ path, value, prevValue }) {
const [,setErrorMsg] = getStore.errorMsg()
setErrorMsg(value > 99 ? 'price should be lower than $99')
function onAfterPriceChange({ store, prevStore }) {
// ...
}
```

- If the intention is to register events that last forever, it has to be done within the `createStore`:

```js
const { getStore } = createStore(initialStore, onAfterUpdate) //

function onAfterUpdate({ path, value, prevValue }) {
if(path === 'cart.price') {
const [,setErrorMsg] = getStore.errorMsg()
setErrorMsg(value > 99 ? 'price should be lower than $99')
}
}
const { getStore } = createStore(initialStore, onAfterUpdate); //

function onAfterUpdate({ store, prevStore }) {
// ..
}
```

Very useful to use it:
Expand Down Expand Up @@ -357,18 +353,21 @@ There are 2 ways to register:
- **Permanent** events: Inside `createStore`. This event will always be executed for each change made within the store.

```js
export const { useStore, getStore } = createStore(initialStore, onAfterUpdate);

function onAfterUpdate({ path, prevValue, value }) {
if (path !== "count") return;

const [, setCount] = getStore.count();
const [errorMsg, setErrorMsg] = getStore.errorMsg();
export const { useStore, getStore } = createStore(
initialStore,
onAfterUpdate
);

if (value > 99) {
setCount(prevValue);
function onAfterUpdate({ store, prevStore }) {
// Add an error msg
if (store.count > 99 && !store.errorMsg) {
const [, setErrorMsg] = getStore.errorMsg();
setErrorMsg("The count value should be lower than 100");
} else if (errorMsg) {
return;
}
// Remove error msg
if (store.count >= 99 && store.errorMsg) {
const [, setErrorMsg] = getStore.errorMsg();
setErrorMsg();
}
}
Expand All @@ -381,14 +380,15 @@ There are 2 ways to register:
const [count, setCount] = useStore.count(0, onAfterUpdate);
const [errorMsg, setErrorMsg] = useStore.errorMsg();

// The event lasts as long as this component lives, but
// it's also executed if the "count" property is updated
// elsewhere.
function onAfterUpdate({ value, prevValue }) {
if (value > 99) {
setCount(prevValue);
// The event lasts as long as this component lives
function onAfterUpdate({ store, prevStore }) {
// Add an error msg
if (store.count > 99 && !store.errorMsg) {
setErrorMsg("The count value should be lower than 100");
} else if (errorMsg) {
return;
}
// Remove error msg
if (store.count >= 99 && store.errorMsg) {
setErrorMsg();
}
}
Expand Down Expand Up @@ -569,16 +569,15 @@ export const { useStore, getStore } = createStore(
onAfterUpdate
);

function onAfterUpdate({ path }) {
if (path === "cart" || path.startsWith("cart.")) updateCalculatedCartProps();
}

// Price always will be items.length * 3
function updateCalculatedCartProps() {
const [items] = getStore.cart.items();
const [price, setPrice] = getStore.cart.price();
function onAfterUpdate({ store }) {
const { items, price } = store.cart;
const calculatedPrice = items.length * 3;
if (price !== calculatedPrice) setPrice(calculatedPrice);

// Price always will be items.length * 3
if (price !== calculatedPrice) {
const [, setPrice] = getStore.cart.price();
setPrice(calculatedPrice);
}
}
```

Expand Down
20 changes: 9 additions & 11 deletions package/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ export default function createStore(defaultStore = {}, callback) {

useEffect(() => {
subscription._subscribe(path, forceRender);
subscription._subscribe(path, callback);
subscription._subscribe(DOT, callback);
return () => {
subscription._unsubscribe(path, forceRender);
subscription._unsubscribe(path, callback);
subscription._unsubscribe(DOT, callback);
};
}, []);
}
Expand All @@ -126,9 +126,9 @@ export default function createStore(defaultStore = {}, callback) {
*/
function updateField(path = '') {
let fieldPath = Array.isArray(path) ? path : path.split(DOT);
let prevValue = getField(allStore, fieldPath);

return (newValue) => {
let prevStore = allStore;
let value = newValue;

if (typeof newValue === 'function') {
Expand All @@ -143,10 +143,8 @@ export default function createStore(defaultStore = {}, callback) {

// Notifying to all subscribers
subscription._notify(DOT+path, {
path: fieldPath.join(DOT),
value,
prevValue,
getStore,
prevStore,
store: allStore,
});
};
}
Expand Down Expand Up @@ -200,10 +198,10 @@ function createSubscription() {
if (!listeners[path]) listeners[path] = new Set();
listeners[path].add(listener);
},
_notify(path, param) {
Object.keys(listeners).forEach((listenersKey) => {
if (path.startsWith(listenersKey) || listenersKey.startsWith(path)) {
listeners[listenersKey].forEach((listener) => listener(param));
_notify(path, params) {
Object.keys(listeners).forEach((listenerKey) => {
if (path.startsWith(listenerKey) || listenerKey.startsWith(path)) {
listeners[listenerKey].forEach((listener) => listener(params));
}
});
},
Expand Down
Loading

0 comments on commit 8f05b6d

Please sign in to comment.