Skip to content

Commit

Permalink
Merge pull request #6 from mausworks/release/1.0.3
Browse files Browse the repository at this point in the history
Release 1.0.3
  • Loading branch information
mausworks authored Dec 7, 2023
2 parents 5d22533 + 8aaff68 commit def61f4
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 54 deletions.
1 change: 0 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.github/
src/
test/
.gitignore
bun.lockb
tsconfig.build.json
Expand Down
14 changes: 14 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ISC License

Copyright 2023 Rasmus Wennerström <[email protected]>

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 changes: 15 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ const Pagination = ({ maxPage }: PaginationProps) => {
};
```

Real life applications are often more complex, though.

Let's add the `patch` function using the `objectAPI` plugin so we can handle partial updates:
Real life applications are often more complex, though, so let's add the `patch` function from the object plugin to handle partial updates:

```tsx
import storeHook from "tyin/hook";
Expand All @@ -81,7 +79,7 @@ const UserNameInput = () => {
};
```

Tyin also ships with a convenience plugin for arrays—because not every state is an object.
Tyin also ships with a convenience plugin for arrays—because not every state is an object!

In this example, we will add it, along with the persist plugin,
and a custom setter called `complete`:
Expand Down Expand Up @@ -169,21 +167,20 @@ For example: Not every store needs a plugin, so the `StoreAPI` isn't readily ext
To get an estimate on the bundle size you can run:

```sh
bun run test/bundle-size/estimate.ts
bun run src/test/size.ts
```

This is the current output:

```txt
export-all.js: 1714 bytes (898 gzipped)
export-object.js: 1373 bytes (766 gzipped)
export-object-no-persist.js: 967 bytes (552 gzipped)
hook.js: 529 bytes (350 gzipped)
plugin-persist.js: 415 bytes (304 gzipped)
plugin-array.js: 332 bytes (190 gzipped)
plugin-object.js: 286 bytes (226 gzipped)
store.js: 245 bytes (212 gzipped)
extend.js: 167 bytes (140 gzipped)
export-all: 1619 bytes, 832 gzipped
export-common: 1309 bytes, 722 gzipped
hook: 529 bytes, 350 gzipped
plugin-persist: 415 bytes, 304 gzipped
plugin-array: 332 bytes, 190 gzipped
plugin-object: 286 bytes, 226 gzipped
store: 245 bytes, 212 gzipped
extend: 167 bytes, 138 gzipped
```

So, that means if you import everything; Tyin will add ~900 bytes to your bundle size,
Expand All @@ -202,17 +199,17 @@ I picked these frameworks, because I think most people are familiar with them.
| **Zustand** | Create store, define setter functions on the state \*\* | Use store hook | Call setter functions on the state |
| **Redux** | Create store, define setter actions, add provider to app | Use useDispatch | Dispatch setter actions with useDispatch |

> **\*** = It is uncommon to have to define your own setter functions on the store when using Tyin.
> **\*** = You rarely define your own setter functions when using Tyin.
> These are provided by plugins such as `tyin/plugin-object` instead.
> **\*\*** This is technically not needed, [but it is the recommended usage](https://docs.pmnd.rs/zustand/getting-started/introduction).
> **\*\*** = This is technically not needed, [but it is the recommended usage](https://docs.pmnd.rs/zustand/getting-started/introduction).
## Project motivation

This project is inspired by [zustand](https://github.com/pmndrs/zustand)—I love zustand.
I have, however, been "using it wrong" while working on [dott.bio](https://get.dott.bio).

Most of my stores—after refactoring—now look like this:
Most of the stores—after refactoring—now look like this:

```tsx
const useTourState = create(() => ({ started: false, step: 0 }));
Expand Down Expand Up @@ -240,7 +237,7 @@ If you can look beyond _"that initial irk"_, you may start seeing some benefits
Remember: you can now access and update the store from anywhere, and your components will simply comply—magic! 🪄

Another pain point I had with using zustand "the vanilla way" was that I kept declaring
the same couple of state setter over and over again for each store.
the same couple of state setters over and over again for each store.
This is what finally drove me to just call `setState` directly instead since it's versatile enough for most use cases:

```ts
Expand Down
2 changes: 1 addition & 1 deletion src/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type Plugin<T extends object, P = void> = (host: T) => P;
/** An object that can be extended through plugins. */
export type Extensible<T extends object> = T & {
/**
* Returns a copy of this extensible object with the properties from the plugin added.
* Adds the properties from the plugin to the object, and returns it.
* @param plugin A function that receives the object and returns additional properties.
*/
with: <P>(plugin: Plugin<T, P>) => Extensible<T & P>;
Expand Down
16 changes: 11 additions & 5 deletions src/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ export type StateSelectorHook<T> = {
/** Returns the current state. */
(): T;
/**
* Returns a value from the state.
* Selects a value from the state.
* @param select A function that returns a value from the state.
* @param equals (Optional) Compares equality of the previously selected and next value.
* If the values are equal, the hook will not re-render.
* @param equals (Optional) Compare the previously selected and next value:
* If the values are equal between updates,
* the hook will not re-render.
* The default is `Object.is`.
* @example
* ```ts
* const a = useExample((state) => state.a);
* const b = useExample((state) => state.b, (prev, next) => next > prev);
* const size = useExample(() => useExample().size());
* ```
*/
<U>(select: StateSelector<T, U>, equals?: StateComparer<U>): U;
};
Expand All @@ -38,7 +45,6 @@ function bindHook<T extends AnyState>(
equals: StateComparer<any> = Object.is
) => {
const oldRef = React.useRef<any>();

const select = () => {
const oldValue = oldRef.current;
const newValue = selector(store.get());
Expand All @@ -61,7 +67,7 @@ function bindHook<T extends AnyState>(
* The returned value is both a function and a `StoreAPI` object,
* which means that you call `set` directly on the hook to update the state.
*
* **Tip:** Add new setter functions by using `tyin/extend` and plugins
* **Tip:** Add new setter functions with `tyin/extend` and plugins
* such as `tyin/plugin-object` or `tyin/plugin-array`.
* They provide a convenient API that promotes reuse,
* which helps with reducing your overall bundle size!
Expand Down
7 changes: 7 additions & 0 deletions src/test/_export-all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This is here to measure the bundle size of the library:
export * from "../hook";
export * from "../store";
export * from "../extend";
export * from "../plugin-array";
export * from "../plugin-object";
export * from "../plugin-persist";
5 changes: 5 additions & 0 deletions src/test/_export-common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// This is here to measure the bundle size of the library:
export * from "../hook";
export * from "../extend";
export * from "../plugin-object";
export * from "../plugin-persist";
9 changes: 0 additions & 9 deletions src/test/export-all.ts

This file was deleted.

6 changes: 0 additions & 6 deletions src/test/export-object-no-persist.ts

This file was deleted.

7 changes: 0 additions & 7 deletions src/test/export-object.ts

This file was deleted.

17 changes: 10 additions & 7 deletions test/bundle-size/estimate.ts → src/test/size.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import fs from "fs/promises";
import zlib from "zlib";
import fs from "fs/promises";

await Bun.build({
entrypoints: [
"src/test/export-all.ts",
"src/test/export-object.ts",
"src/test/export-object-no-persist.ts",
"src/test/_export-all.ts",
"src/test/_export-common.ts",
"src/hook.ts",
"src/store.ts",
"src/extend.ts",
"src/plugin-object.ts",
"src/plugin-array.ts",
"src/plugin-persist.ts",
],
outdir: "test/bundle-size/dist",
outdir: "src/test/.dist",
external: ["react"],
minify: true,
});
Expand All @@ -36,10 +35,14 @@ const measureDirectory = async (path: string) => {
}
};

await measureDirectory("test/bundle-size/dist");
await measureDirectory("src/test/.dist");

const nice = (name: string) => name.replace(/_/g, "").replace(/\.js$/, "");

for (const [name, { size, gzipped }] of Object.entries(sizes).sort(
(a, b) => b[1].size - a[1].size
)) {
console.log(`${name}: ${size} bytes (${gzipped} gzipped)`);
console.log(`${nice(name)}: ${size} bytes, ${gzipped} gzipped`);
}

await fs.rm("src/test/.dist", { recursive: true });

0 comments on commit def61f4

Please sign in to comment.