Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove confusing reset feature #44

Merged
merged 4 commits into from
Nov 14, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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-canary.1",
"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
62 changes: 21 additions & 41 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 @@ -61,26 +60,19 @@ export default function createStore(defaultStore = {}, callback) {
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
52 changes: 36 additions & 16 deletions tests/example.counter.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, useState} from 'react';
import {Component, useEffect, useRef, useState} from 'react';
import {render, waitFor, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';

Expand All @@ -8,16 +8,17 @@ import createStore from '../package/index';

describe('Example: Counter', () => {
it('should work with a simple counter', () => {
const {useStore} = createStore({count: 0});
const initialStore = {count: 0};
const {useStore} = createStore(initialStore);

function Counter() {
const [count, setCount, resetCount] = useStore.count();
const [count, setCount] = useStore.count();
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(initialStore.count)}>reset</button>
</div>
);
}
Expand Down Expand Up @@ -63,17 +64,18 @@ describe('Example: Counter', () => {
});

it('should work with a counter in a class component', async () => {
const {useStore, withStore} = createStore({count: 0});
const initialStore = {count: 0};
const {useStore, withStore} = createStore(initialStore);

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(initialStore.count)}>reset</button>
</div>
);
}
Expand Down Expand Up @@ -127,11 +129,12 @@ describe('Example: Counter', () => {
});

it('should work with a counter in a class component: with all store', async () => {
const {useStore, withStore} = createStore({count: 0});
const initialStore = {count: 0};
const {useStore, withStore} = createStore(initialStore);

class Counter extends Component {
render() {
const [store, setStore, resetStore] = this.props.store;
const [store, setStore] = this.props.store;
return (
<div>
<h1>{store.count}</h1>
Expand All @@ -145,7 +148,7 @@ describe('Example: Counter', () => {
>
-
</button>
<button onClick={resetStore}>reset</button>
<button onClick={() => setStore(initialStore)}>reset</button>
</div>
);
}
Expand Down Expand Up @@ -203,13 +206,28 @@ describe('Example: Counter', () => {
const {useStore} = createStore({anotherValue: ''});

function Counter() {
const [count, setCount, resetCount] = useStore.count();
const initialCountValue = useRef();
const [count, setCount] = useStore.count();

useEffect(() => {
if (
initialCountValue.current === undefined &&
count !== undefined
) {
initialCountValue.current = count;
}
}, [count]);

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(initialCountValue.current)}
>
reset
</button>
</div>
);
}
Expand Down Expand Up @@ -272,26 +290,27 @@ describe('Example: Counter', () => {
it('should work with a counter in a class component: as a new value (not defined on the store)',
async () => {
const {useStore, withStore} = createStore();
const initialCount = 0;

class Counter extends Component {
// Forcing update to verify that the initial value is not overwritten
componentDidMount() {
this.forceUpdate();
}
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(initialCount)}>reset</button>
</div>
);
}
}

const CounterWithStore = withStore.counter.count(Counter, 0);
const CounterWithStore = withStore.counter.count(Counter, initialCount);

function DisplayCounter() {
const [count] = useStore.counter.count();
Expand Down Expand Up @@ -339,7 +358,8 @@ describe('Example: Counter', () => {
});

it('Should increase the value using a form with getStore', async () => {
const {useStore, getStore} = createStore({count: 0});
const initialStore = {count: 0};
const {useStore, getStore} = createStore(initialStore);

function CountForm() {
const [newCount, setNewCount] = useState(0);
Expand Down
6 changes: 3 additions & 3 deletions tests/example.todolist.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ describe('Example: Todo list', () => {
}

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

function reset() {
resetTodo(); // Reset with initial value
setDone([]); // Reset with an empty array
setTodo([]);
setDone([]);
}

renderTodoList();
Expand Down
Loading