Add documentation for keys (list rendering)
nbgraham opened this issue · comments
If you have a list of data elements and then change the order of them, the DOM gets out of sync and can render more elements than actually exist in the data.
I wrote a simple todo app with the ability to mark a todo as done. Done todos are sorted to the bottom.
I only had four total todos and marked them done in an odd order. Once I marked them all as done, I got duplicate DOM elements leading to 6 list items, even though the data list still only had 4 items.
This doesn't alway happen at the same time, but it does happen consistently.
I think a "key" is needed to track identical DOM elements across renders, like React.
import { r, t, w } from "@arrow-js/core";
type Todo = {
label: string;
isDone: boolean;
};
const App = () => {
const data = r({
todos: [] as Todo[],
});
w(() => console.log("todos changes", JSON.stringify(data.todos)));
const addTodo = (label: string) => {
console.log("add todo", label);
const newTodo: Todo = { label, isDone: false };
data.todos = [...data.todos, newTodo] as any;
};
const markDone = (todo: Todo) => () => {
console.log("mark done", todo);
data.todos = data.todos.map((t) =>
t === todo ? { ...t, isDone: true } : t
) as any;
};
const sortedTodos = () => {
return [...data.todos].sort((a, b) => {
if (a.isDone === b.isDone) {
return 0;
}
return a.isDone ? 1 : -1;
});
};
return t`
<h1>Todo List</h1>
<input type="text" @keyup="${(e: KeyboardEvent) => {
if (e.code === "Enter") {
const target = e.target as HTMLInputElement;
addTodo(target.value);
target.value = "";
}
}}">
<ul>
${() =>
sortedTodos().map((todo) =>
TodoItem({ todo, markDone: markDone(todo) })
)}
</ul>
`;
};
const TodoItem = ({ todo, markDone }: { todo: Todo; markDone: () => void }) => {
const style = () => {
if (todo.isDone) {
return `text-decoration: line-through`;
}
return "";
};
return t`
<li style=${style}><button @click=${markDone}>Done</button>${todo.label}</li>
`;
};
const appElement = document.getElementById("app");
App()(appElement);
Ahh, this is actually in there, it just isn't properly documented.
t`<li>item</li>`.key(yourKey)
Ah gotcha, that worked for me, thanks!
Do you want me to close this or keep it open to track the need for documentation?
Ok docs have been added for this 👍