From d52bf68771bb652c01c27ab4c54689c2f61e4cb0 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 26 Sep 2023 19:29:48 +0200 Subject: [PATCH] Add demo and fill readme --- packages/mutable/README.md | 115 +++++++++++++++++++++++++++++++-- packages/mutable/dev/index.tsx | 80 +++++++++++++++++++---- packages/mutable/package.json | 16 +++-- 3 files changed, 186 insertions(+), 25 deletions(-) diff --git a/packages/mutable/README.md b/packages/mutable/README.md index bbc069d31..af86bf4e0 100644 --- a/packages/mutable/README.md +++ b/packages/mutable/README.md @@ -9,29 +9,130 @@ [![version](https://img.shields.io/npm/v/@solid-primitives/mutable?style=for-the-badge)](https://www.npmjs.com/package/@solid-primitives/mutable) [![stage](https://img.shields.io/endpoint?style=for-the-badge&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsolidjs-community%2Fsolid-primitives%2Fmain%2Fassets%2Fbadges%2Fstage-0.json)](https://github.com/solidjs-community/solid-primitives#contribution-process) -A sample primitive that is made up for templating with the following options: +A primitive for creating a mutable store proxy object. An alternative to `createStore` from `"solid-js/store"`. -`createPrimitiveTemplate` - Provides a getter and setter for the primitive. +- [`createMutable`](#createMutable) - Creates a mutable store proxy object. +- [`modifyMutable`](#modifyMutable) - Helper for applying store mutation utilities - like `produce` or `reconcile` from `"solid-js/store"` - to a mutable store. ## Installation ```bash npm install @solid-primitives/mutable # or -yarn add @solid-primitives/mutable -# or pnpm add @solid-primitives/mutable +# or +yarn add @solid-primitives/mutable +``` + +## `createMutable` + +```ts +import { createMutable } from "@solid-primitives/mutable"; + +declare function createMutable(state: T): T; +``` + +Creates a new mutable Store proxy object. Stores only trigger updates on values changing. Tracking is done by intercepting property access and automatically tracks deep nesting via proxy. + +Useful for integrating external systems or as a compatibility layer with MobX/Vue. + +> **Note:** A mutable state can be passed around and mutated anywhere, which can make it harder to follow and easier to break unidirectional flow. It is generally recommended to use `createStore` instead. The `produce` modifier can give many of the same benefits without any of the downsides. + +```js +const state = createMutable(initialValue); + +// read value +state.someValue; + +// set value +state.someValue = 5; + +state.list.push(anotherValue); +``` + +Mutables support setters along with getters. + +```js +const user = createMutable({ + firstName: "John", + lastName: "Smith", + get fullName() { + return `${this.firstName} ${this.lastName}`; + }, + set fullName(value) { + [this.firstName, this.lastName] = value.split(" "); + }, +}); ``` -## How to use it +## `modifyMutable` ```ts -const [value, setValue] = createPrimitiveTemplate(false); +import { modifyMutable } from "@solid-primitives/mutable"; + +declare function modifyMutable(mutable: T, modifier: (state: T) => T): void; +``` + +This helper function simplifies making multiple changes to a mutable Store +(as returned by [`createMutable`](#createmutable)) +in a single [`batch`](#batch), +so that dependant computations update just once instead of once per update. + +The first argument is the mutable Store to modify, +and the second argument is a Store modifier such as those returned by +[`reconcile`](#reconcile) or [`produce`](#produce). +_(If you pass in your own modifier function, beware that its argument is +an unwrapped version of the Store.)_ + +For example, suppose we have a UI depending on multiple fields of a mutable: + +```tsx +const state = createMutable({ + user: { + firstName: "John", + lastName: "Smith", + }, +}); + +

Hello {state.user.firstName + " " + state.user.lastName}

; +``` + +Modifying _n_ fields in sequence will cause the UI to update _n_ times: + +```ts +state.user.firstName = "Jake"; // triggers update +state.user.lastName = "Johnson"; // triggers another update +``` + +To trigger just a single update, we could modify the fields in a `batch`: + +```ts +batch(() => { + state.user.firstName = "Jake"; + state.user.lastName = "Johnson"; +}); +``` + +`modifyMutable` combined with `reconcile` or `produce` +provides two alternate ways to do similar things: + +```ts +// Replace state.user with the specified object (deleting any other fields) +modifyMutable(state.user, reconcile({ + firstName: "Jake", + lastName: "Johnson", +}); + +// Modify two fields in batch, triggering just one update +modifyMutable(state.user, produce((u) => { + u.firstName = "Jake"; + u.lastName = "Johnson"; +}); ``` ## Demo -You can use this template for publishing your demo on CodeSandbox: https://codesandbox.io/s/solid-primitives-demo-template-sz95h +[Deployed example](https://primitives.solidjs.community/playground/mutable) | [Source code](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutable/dev/index.tsx) ## Changelog diff --git a/packages/mutable/dev/index.tsx b/packages/mutable/dev/index.tsx index e2e5113e6..db53bd740 100644 --- a/packages/mutable/dev/index.tsx +++ b/packages/mutable/dev/index.tsx @@ -1,18 +1,76 @@ -import { Component, createSignal } from "solid-js"; +import { onMount, type Component } from "solid-js"; +import { For } from "solid-js/web"; +import { createMutable } from "../src/index.js"; + +interface Todo { + id: number; + text: string; + completed: boolean; +} + +const initial_todos: Todo[] = [ + { + id: 0, + text: "Learn Solid", + completed: true, + }, + { + id: 1, + text: "Learn about createMutable", + completed: false, + }, +]; +let last_todo_id = 0; const App: Component = () => { - const [count, setCount] = createSignal(0); - const increment = () => setCount(count() + 1); + const todos = createMutable(initial_todos); + + function addTodo(text: string) { + todos.push({ id: ++last_todo_id, text, completed: false }); + } + let input!: HTMLInputElement; return ( -
-
-

Counter component

-

it's very important...

- -
+
+
{ + e.preventDefault(); + if (!input.value.trim()) return; + addTodo(input.value); + input.value = ""; + }} + > + + +
+ + {todo => ( +
{ + onMount(() => { + el.animate( + [ + { opacity: 0, transform: "translateX(-100%)" }, + { opacity: 1, transform: "translateX(0)" }, + ], + { duration: 500, fill: "forwards" }, + ); + }); + }} + > + (todo.completed = !todo.completed)} + /> + + {todo.text} + +
+ )} +
); }; diff --git a/packages/mutable/package.json b/packages/mutable/package.json index 9fe561ad3..52819724f 100644 --- a/packages/mutable/package.json +++ b/packages/mutable/package.json @@ -1,8 +1,8 @@ { "name": "@solid-primitives/mutable", - "version": "0.0.100", - "description": "A template primitive example.", - "author": "Your Name ", + "version": "1.0.0", + "description": "A primitive for creating a mutable store, an alternative to Solid's createStore.", + "author": "Damian Tarnawski ", "contributors": [], "license": "MIT", "homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutable#readme", @@ -15,15 +15,17 @@ }, "primitive": { "name": "mutable", - "stage": 0, + "stage": 3, "list": [ - "createPrimitiveTemplate" + "createMutable", + "modifyMutable" ], - "category": "Display & Media" + "category": "Reactivity" }, "keywords": [ "solid", - "primitives" + "primitives", + "store" ], "private": false, "sideEffects": false,