Skip to content

Commit

Permalink
Implement useUnfragmentedStore
Browse files Browse the repository at this point in the history
  • Loading branch information
aralroca committed Mar 12, 2021
1 parent 080cd64 commit ef7ef1a
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
.vscode
node_modules
dist
97 changes: 91 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Fragmented Store
</p>

<p align="center">
Tiny (~400 B) and simple (P)React <b>state management library</b>
Tiny (~500 B) and simple (P)React <b>state management library</b>
</p>
<p align="center">
Store update -> <b>only</b> components that use the updated property are rendered.
Expand Down Expand Up @@ -47,9 +47,9 @@ npm install fragmented-store --save
```js
import createStore from "fragmented-store";

const { Provider, useUsername, useAge } = createStore({
const { Provider, useUsername, useAge, useUnfragmentedStore } = createStore({
username: "Aral",
age: 31
age: 30
});

export default function App() {
Expand All @@ -58,6 +58,7 @@ export default function App() {
<Title />
<UpdateTitle />
<Age />
<AllStore />
</Provider>
);
}
Expand All @@ -76,7 +77,7 @@ function UpdateTitle() {
console.log("render UpdateTitle");

return (
<button onClick={() => setUsername((u) => u + "a")}>
<button onClick={() => setUsername((s) => s + "a")}>
Update {username}
</button>
);
Expand All @@ -85,15 +86,99 @@ function UpdateTitle() {
function Age() {
const [age, setAge] = useAge();

console.log("render Age");
console.log("render age");

return (
<div>
<div>{age}</div>
<button onClick={() => setAge((a) => a + 1)}>Inc age</button>
<button onClick={() => setAge((s) => s + 1)}>Inc age</button>
</div>
);
}

function AllStore() {
const [store, update] = useUnfragmentedStore();

console.log({ store }); // all store

return (
<button onClick={() => update({ age: 30, username: "Aral" })}>Reset</button>
);
}
```

### Provider

The `Provider` is required for any of its child components to consume fragmented-store hooks.

```js
import createStore from "fragmented-store";

const { Provider } = createStore({
username: "Aral",
age: 30
});

function App() {
return (
<Provider>
{/* rest */}
</Provider>
);
}
```

### Fragmented store

The power of this library is that you can use fragmented parts of the store, so if a component uses only one field of the store, it will only re-render again if there is a change in this particular field and it will not render again if the other fields change.

For each of the fields of the store, there is a hook with its name, examples:

- username 👉 `useUsername`
- age 👉 `useAge`
- anotherExample 👉 `useAnotherExample`

```js
import createStore from "fragmented-store";

const { useUsername } = createStore({
username: "Aral",
age: 30
});

function FragmentedExample() {
const [username, setUsername] = useUsername();

return (
<button onClick={() => setUsername("AnotherUserName")}>
Update {username}
</button>
);
}
```

### Unfragmented store

The advantage of this library is to use the store in a fragmented way. Even so, there are cases when we want to reset the whole store or do more complex things. For these cases, we can use the hook `useUnfragmentedStore`.

```js
import createStore from "fragmented-store";

const { useUnfragmentedStore } = createStore({
username: "Aral",
age: 30
});

function UnfragmentedExample() {
const [store, update] = useUnfragmentedStore();

return (
<>
<h1>{state.username}, {state.age}</h1>
<button onClick={() => update({ age: 30, username: "Aral" })}>Reset</button>
</>
);
}
```

## Demo
Expand Down
29 changes: 23 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import React, { useState, useContext, createContext } from "react";

export default function createStore(store = {}) {
const storeUtils = Object.keys(store).reduce((o, key) => {
const fieldCapitalized = `${key[0].toUpperCase()}${key.slice(
1,
key.length
)}`;
const keys = Object.keys(store);
const capitalize = (k) => `${k[0].toUpperCase()}${k.slice(1, k.length)}`;

const storeUtils = keys.reduce((o, key) => {
const context = createContext(store[key]);

return {
...o,
contexts: [...(o.contexts || []), { context, key }],
[`use${fieldCapitalized}`]: () => useContext(context)
[`use${capitalize(key)}`]: () => useContext(context)
};
}, {});

Expand All @@ -34,5 +33,23 @@ export default function createStore(store = {}) {
return <Component>{children}</Component>;
};

storeUtils.useUnfragmentedStore = () => {
const state = {};
const updates = {};
keys.forEach((k) => {
const [s, u] = storeUtils[`use${capitalize(k)}`]();
state[k] = s;
updates[k] = u;
});

function updater(newState) {
const s =
typeof newState === "function" ? newState(state) : newState || {};
Object.keys(s).forEach((k) => updates[k] && updates[k](s[k]));
}

return [state, updater];
};

return storeUtils;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"module": "dist/index.module.js",
"unpkg": "dist/index.umd.js",
"scripts": {
"build": "microbundle --jsx React.createElement --no-compress --sourcemap",
"build": "microbundle --jsx React.createElement",
"dev": "microbundle watch",
"prepublish": "yarn build"
},
Expand Down

0 comments on commit ef7ef1a

Please sign in to comment.