Last active
August 21, 2025 14:47
-
-
Save Ciantic/4dabaa23abfebadad37645a468753a18 to your computer and use it in GitHub Desktop.
SolidJS TODO example with logical createStore and methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { render } from "solid-js/web"; | |
import { For, createSignal } from "solid-js"; | |
import { createStore, produce } from "solid-js/store"; | |
/** | |
* Reusable todo store, with logical state change functions | |
*/ | |
function createTodos(initialTitle: string) { | |
const createTodo = (id: number, task: string) => ({ | |
id, | |
task, | |
done: false, | |
check(value: boolean) { | |
setStore("todos", (f) => f.id == this.id, "done", value); | |
}, | |
remove() { | |
setStore("todos", (f) => f.filter((i) => i.id !== this.id)); | |
}, | |
}); | |
const [store, setStore] = createStore({ | |
title: initialTitle, | |
todos: [createTodo(1, "Okay start adding todos")], | |
}); | |
return { | |
todos: () => store.todos, | |
title: () => store.title, | |
addTodo: (task: string) => { | |
if (!task) { | |
return; | |
} | |
setStore( | |
produce((mutable) => { | |
mutable.todos.push(createTodo(Math.random(), task)); | |
}), | |
); | |
// Alternatively with setStore | |
// setStore("todos", (f) => [...f, createTodo(Math.random(), task)]); | |
// setStore("todos", store.todos.length, createTodo(Math.random(), task)]); | |
}, | |
// This is not needed, as each task has logical `check()` function | |
// checkTodo: (i: number, value: boolean) => { | |
// setStore("todos", i, "done", value); | |
// }, | |
}; | |
} | |
function Todos() { | |
const [value, setValue] = createSignal("Buy milk"); | |
const todoManager = createTodos("Groceries"); | |
return ( | |
<div> | |
{todoManager.title()} | |
<ul> | |
<For each={todoManager.todos()}> | |
{(task, i) => ( | |
<li> | |
<label style={{ "user-select": "none", cursor: "pointer" }}> | |
<input | |
type="checkbox" | |
checked={task.done} | |
onInput={(e) => task.check(e.target.checked)} | |
/>{" "} | |
<span | |
style={{ | |
"text-decoration": task.done ? "line-through" : "none", | |
}} | |
> | |
{task.task} | |
</span> | |
</label> | |
<button type="button" onClick={() => task.remove()}> | |
Remove | |
</button> | |
</li> | |
)} | |
</For> | |
</ul> | |
<input | |
type="text" | |
value={value()} | |
onInput={(e) => { | |
setValue(e.target.value ?? ""); | |
}} | |
/> | |
<button type="button" onClick={() => todoManager.addTodo(value())}> | |
Add TODO | |
</button> | |
</div> | |
); | |
} | |
render(() => <Todos />, document.getElementById("app")!); | |
// It is also possible to nest stores inside stores, e.g. there could be: | |
/* | |
function createTask(id: number, task: string) { | |
const [state, setTask] = createStore({ | |
id, | |
task, | |
done: false, | |
check(value: boolean) { | |
setTask("done", value); | |
}, | |
}); | |
return state; | |
} | |
*/ | |
// It could be put inside another store in array... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment