Skip to content

Commit

Permalink
Remove confusing reset feature (#44)
Browse files Browse the repository at this point in the history
* Remove confusing reset feature

* apply changes on examples folder

* Update docs

* Update package version

Co-authored-by: Danielo Artola <[email protected]>
  • Loading branch information
aralroca and danielart authored Nov 14, 2021
1 parent f7e43f6 commit 1e98db5
Show file tree
Hide file tree
Showing 11 changed files with 878 additions and 865 deletions.
55 changes: 16 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ _Tiny, easy and powerful **React state management** library_
- [4. Register events after an update 🚦](#register-events-after-an-update-)
- [5. How to... 🧑‍🎓](#how-to-)
- [Add a new store property](#add-a-new-store-property)
- [Reset a store property](#reset-a-store-property)
- [Reset all the store](#reset-all-the-store)
- [Use more than one store](#use-more-than-one-store)
- [Update several portions avoiding rerenders in the rest](#update-several-portions-avoiding-rerenders-in-the-rest)
- [Define calculated properties](#define-calculated-properties)
Expand Down Expand Up @@ -225,7 +223,7 @@ Is an `Array` with **3** items:
| ------------ | ---------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| value | `any` | The value of the store portion indicated with the proxy. | A store portion <div>`const [price] = useStore.cart.price()`</div>All store: <div> `const [store] = useStore()`</div> |
| update value | `function` | Function to update the store property indicated with the proxy. | Updating a store portion:<div>`const [count, setCount] = useStore.count(0)`</div>Way 1:<div>`setCount(count + 1)`</div>Way 1:<div>`setCount(c => c + 1)`</div><div>-------</div>Updating all store:<div>`const [store, updateStore] = useStore()`</div>Way 1:<div>`updateStore({ ...store, count: 2 }))`</div>Way 1:<div>`updateStore(s => ({ ...s, count: 2 }))`</div> |
| reset value | `function` | Function that reset the store property indicated with the proxy to their initial value. | Reset store portion:<div>`const [,,resetCount] = useStore.count()`</div><div>`resetCount()`</div><small>_Put counter to 0 again (initial value defined inside the `createStore`)._</small><div>-------</div>Reset all store:<div>`const [,,resetStore] = useStore()`</div><div>`resetStore()`</div><small>_All store portions to their initial values._</small> |
|

### getStore helper

Expand Down Expand Up @@ -255,18 +253,25 @@ It works exactly like `useStore` but with **some differences**:
Very useful to use it:

- **Outside components**: helpers, services, etc.
- **Inside components**: Avoiding rerenders if you want to consume it inside events, when you only use the updaters `const [, setCount, resetCount] = getStore.count()`, etc.
- **Inside components**: Avoiding rerenders if you want to consume it inside events, when you only use the updater `const [, setCount] = getStore.count()`, etc.

Example:

```js
import { useState } from "react";

const { getStore } = createStore({ count: 0 });
const initialStore = { count: 0 }
const { getStore } = createStore(initialStore);

function Example1() {
const resetStore = getStore()[2];
return <button onClick={resetStore}>Reset store</button>;
return (
<button onClick={() => {
const [, setStore] = getStore();
setStore(initialStore)
}}>
Reset store
</button>
);
}

function Example2() {
Expand Down Expand Up @@ -303,13 +308,13 @@ const { withStore } = createStore();

class Counter extends Component {
render() {
const [count, setCount, resetCount] = this.props.store;
const [count, setCount] = this.props.store;
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount((v) => v + 1)}>+</button>
<button onClick={() => setCount((v) => v - 1)}>-</button>
<button onClick={resetCount}>reset</button>
<button onClick={() => setCount(0)}>reset</button>
</div>
);
}
Expand All @@ -326,13 +331,13 @@ const { withStore } = createStore({ count: 0 });

class Counter extends Component {
render() {
const [store, setStore, resetStore] = this.props.store;
const [store, setStore] = this.props.store;
return (
<div>
<h1>{store.count}</h1>
<button onClick={() => setStore({ count: store.count + 1 })}>+</button>
<button onClick={() => setStore({ count: store.count - 1 })}>-</button>
<button onClick={resetStore}>reset</button>
<button onClick={() => setStore({ count: 0 })}>reset</button>
</div>
);
}
Expand Down Expand Up @@ -448,34 +453,6 @@ function CreateProperty() {
}
```

### Reset a store property

You can use the 3th array item from `useStore` / `getStore` / `withStore`. It's a function to return the value to its initial value.

```js
const [item, setItem, resetItem] = useStore.item();
// ...
resetItem();
```

If you only want the reset function and not the value, we recommend using the `getStore` to avoid creating a subscription and avoid unnecessary rerenders.

```js
const [, , resetItem] = getStore.item();
// or...
const resetItem = getStore.item()[2];
```

### Reset all the store

The [same thing](#reset-a-store-property) works to reset the entire store to its initial value.

```js
const [store, setStore, resetStore] = useStore();
// ...
resetStore();
```

### Use more than one store

You can have as many stores as you want. The only thing you have to do is to use as many `createStore` as stores you want.
Expand Down
13 changes: 8 additions & 5 deletions examples/todo-list/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

# Teaful TodoList Example

## How to start :gear:

### Available Scripts

In the project directory, you can run:

npm start
Expand Down Expand Up @@ -72,14 +72,17 @@ Here, we are **not mutating** the original todo but creating a new one and passi

This component list the todo and the done list, so we need both of them from store (and of course, import useStore from our `store.js`)

const [todo, setTodo, resetTodo] = useStore.todo();
const [done, setDone, resetDone] = useStore.done();
const [todo, setTodo] = useStore.todo();
const [done, setDone] = useStore.done();

### Reset lists

This is the easiest point. To reset one element from store, Teaful provide us a reset function at third element on destructure. Having this in mind, the button to reset lists is clear
This is the easiest point. To reset one element from store, we could use setter again (in this case an empty object for each). Having this in mind, the button to reset lists is clear

<button onClick={() => {resetDone();resetTodo();}}>
<button onClick={() => {
setDone({});
setTodo({});
}}>
Reset tasks
</button>

Expand Down
7 changes: 3 additions & 4 deletions examples/todo-list/src/AddTodoTask.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {useState} from 'react';
import {useStore} from './store';
import { useState } from 'react';
import { useStore } from './store';

export default function AddTodoTask() {
const [counter, setCounter] = useState(0);
const [error, setError] = useStore.error();
const [todo, setTodo] = useStore.todo();
const addTask = (e) => {
console.log(JSON.stringify(e.target.children[0].value));
e.preventDefault();
if (e.target.children[0].value.trim() === '') {
setError('Can\'t add empty tasks');
Expand All @@ -15,7 +14,7 @@ export default function AddTodoTask() {

setTodo({
...todo,
[counter]: {text: e.target.children[0].value, id: counter},
[counter]: { text: e.target.children[0].value, id: counter },
});
e.target.children[0].value = '';
setCounter(counter + 1);
Expand Down
18 changes: 9 additions & 9 deletions examples/todo-list/src/TodoList.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
import {useStore} from './store';
import { useStore } from './store';

import Task from './Task';

export default function TodoList() {
const [todo, setTodo, resetTodo] = useStore.todo();
const [done, setDone, resetDone] = useStore.done();
const [todo, setTodo] = useStore.todo();
const [done, setDone] = useStore.done();
const [error] = useStore.error();

const resolve = (task) => {
deleteTask(task.id);
setDone({...done, [task.id]: {...task}});
setDone({ ...done, [task.id]: { ...task } });
};

const unresolve = (task) => {
deleteTask(task.id, true);
setTodo({...todo, [task.id]: {...task}});
setTodo({ ...todo, [task.id]: { ...task } });
};

const deleteTask = (id, resolved) => {
if (resolved) {
const newDoneList = {...done};
const newDoneList = { ...done };
delete newDoneList[id];
setDone(newDoneList);
} else {
const newTodoList = {...todo};
const newTodoList = { ...todo };
delete newTodoList[id];
setTodo(newTodoList);
}
};

const resetAll = () => {
resetTodo();
resetDone();
setTodo({});
setDone({});
};

return (
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "teaful",
"version": "0.7.2",
"version": "0.8.0",
"description": "Tiny, easy and powerful React state management (less than 1kb)",
"license": "MIT",
"keywords": [
Expand Down Expand Up @@ -88,7 +88,7 @@
"eslint-plugin-react-hooks": "4.2.0",
"eslint-plugin-testing-library": "4.12.4",
"jest": "27.2.5",
"microbundle": "0.14.0",
"microbundle": "0.14.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-test-renderer": "17.0.2"
Expand Down
66 changes: 23 additions & 43 deletions package/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default function createStore(defaultStore = {}, callback) {

// Initialize the store and callbacks
let allStore = defaultStore;
let initialStore = defaultStore;

// Add callback subscription
subscription._subscribe(DOT, callback);
Expand Down Expand Up @@ -56,31 +55,24 @@ export default function createStore(defaultStore = {}, callback) {

// ALL STORE (unfragmented):
//
// MODE_GET: let [store, update, reset] = useStore()
// MODE_USE: let [store, update, reset] = getStore()
// MODE_GET: let [store, update] = useStore()
// MODE_USE: let [store, update] = getStore()
if (!path.length) {
let updateAll = updateField();
if (mode === MODE_USE) useSubscription(DOT, callback);
return [allStore, updateAll, resetField()];
return [allStore, updateAll];
}

// .................
// FRAGMENTED STORE:
// .................
let prop = path.join(DOT);
let update = updateField(prop);
let reset = resetField(prop);
let value = getField(allStore, prop);
let value = getField(prop);
let initializeValue = param !== undefined && !existProperty(path);

let initializeValue =
param !== undefined &&
value === undefined &&
getField(initialStore, prop) === undefined;

// Initialize the value if is not defined
if (initializeValue) {
value = param;
initialStore = setField(initialStore, path, value);
allStore = setField(allStore, path, value);
}

Expand All @@ -94,7 +86,7 @@ export default function createStore(defaultStore = {}, callback) {

// MODE_GET: let [price, setPrice] = useStore.cart.price()
// MODE_USE: let [price, setPrice] = getStore.cart.price()
return [value, update, reset];
return [value, update];
},
};
let useStore = new Proxy(() => MODE_USE, validator);
Expand Down Expand Up @@ -134,7 +126,7 @@ export default function createStore(defaultStore = {}, callback) {
let value = newValue;

if (typeof newValue === 'function') {
value = newValue(getField(allStore, path));
value = newValue(getField(path));
}

allStore = path ?
Expand All @@ -151,16 +143,22 @@ export default function createStore(defaultStore = {}, callback) {
};
}

/**
* Reset a field of the store
*
* When resetField(path) is called, the field of the store is
* reset to the initial value.
* @param {string} path
* @return {function}
*/
function resetField(path) {
return () => updateField(path)(getField(initialStore, path));
function getField(path, fn = (a, c) => a?.[c]) {
if (!path) return allStore;
return (Array.isArray(path) ? path : path.split(DOT)).reduce(fn, allStore);
}

function setField(store, [prop, ...rest], value) {
let newObj = Array.isArray(store) ? [...store] : {...store};
newObj[prop] = rest.length ? setField(store[prop], rest, value) : value;
return newObj;
}

function existProperty(path) {
return getField(path, (a, c, index, arr) => {
if (index === arr.length - 1) return c in (a || {});
return a?.[c];
});
}

/**
Expand All @@ -173,24 +171,6 @@ export default function createStore(defaultStore = {}, callback) {
return {useStore, getStore, withStore};
}

// ##########################################################
// ###################### Helpers #########################
// ##########################################################

function getField(store, path) {
if (!path) return store;
return (Array.isArray(path) ? path : path.split(DOT)).reduce(
(a, c) => a?.[c],
store,
);
}

function setField(store = {}, [prop, ...rest], value) {
let newObj = Array.isArray(store) ? [...store] : {...store};
newObj[prop] = rest.length ? setField(store[prop], rest, value) : value;
return newObj;
}

function createSubscription() {
let listeners = {};

Expand Down
Loading

0 comments on commit 1e98db5

Please sign in to comment.