Skip to content

Commit

Permalink
Add demo and fill readme
Browse files Browse the repository at this point in the history
  • Loading branch information
thetarnav committed Sep 26, 2023
1 parent ee0dd78 commit d52bf68
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 25 deletions.
115 changes: 108 additions & 7 deletions packages/mutable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends StoreNode>(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<T>(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",
},
});

<h1>Hello {state.user.firstName + " " + state.user.lastName}</h1>;
```

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
Expand Down
80 changes: 69 additions & 11 deletions packages/mutable/dev/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div class="box-border flex min-h-screen w-full flex-col items-center justify-center space-y-4 bg-gray-800 p-24 text-white">
<div class="wrapper-v">
<h4>Counter component</h4>
<p class="caption">it's very important...</p>
<button class="btn" onClick={increment}>
{count()}
</button>
</div>
<div class="flex min-h-screen flex-col items-center">
<form
class="flex space-x-2"
onSubmit={e => {
e.preventDefault();
if (!input.value.trim()) return;
addTodo(input.value);
input.value = "";
}}
>
<input ref={input} />
<button>Add Todo</button>
</form>
<For each={todos}>
{todo => (
<div
class="w-64 py-1 opacity-0"
ref={el => {
onMount(() => {
el.animate(
[
{ opacity: 0, transform: "translateX(-100%)" },
{ opacity: 1, transform: "translateX(0)" },
],
{ duration: 500, fill: "forwards" },
);
});
}}
>
<input
type="checkbox"
checked={todo.completed}
onchange={() => (todo.completed = !todo.completed)}
/>
<span style={{ "text-decoration": todo.completed ? "line-through" : "none" }}>
{todo.text}
</span>
</div>
)}
</For>
</div>
);
};
Expand Down
16 changes: 9 additions & 7 deletions packages/mutable/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@solid-primitives/mutable",
"version": "0.0.100",
"description": "A template primitive example.",
"author": "Your Name <you@youremail.com>",
"version": "1.0.0",
"description": "A primitive for creating a mutable store, an alternative to Solid's createStore.",
"author": "Damian Tarnawski <gthetarnav@gmail.com>",
"contributors": [],
"license": "MIT",
"homepage": "https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutable#readme",
Expand All @@ -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,
Expand Down

0 comments on commit d52bf68

Please sign in to comment.