Complex Example
Let's create a TODO list app and explore more concepts of the Wompo library.
Todo List app
In this project we will build a simple Todo List app. By doing that you'll get more comfortable using Wompo.
What we want is a component component that will render an input and a button to add a todo item, with a list of already added todos below. We then also want that the Todos that we added are saved in the local storage of the browser, so that they are not lost once we reload the page.
Let's start by creating the TodoList component.
import { defineWompo, html } from 'wompo';
export default function TodoList() {
return html`
<div>
<div>
<input />
<button>+</button>
</div>
<ul>
${/* Here todos will be rendered */ ''}
</ul>
</div>
`;
}
defineWompo(TodoList);The next step can be render a list of fixed todo items inside the ul element. We can do that by creeating a fake todos array, and create an li iten by iterating through it. To do that, you can simply use the .map() method of arrays, and return a new element. But, we already know that the todos component will be stateful, so we can already start using the useState hook, like this:
const initialTodos = ['Complete this tutorial', 'Buy groceries', 'Wash the car'];
export default function TodoList() {
const [todos, setTodos] = useState(initialTodos);
return html`
<div>
${/*... */ ''}
<ul>
${todos.map(
(todo) =>
html`<li>
<button>X</button>
<span>${todo}</span>
</li>`,
)}
</ul>
</div>
`;
}
defineWompo(TodoList);Now it's time to add some interactivity. We should be able to:
- Add a new todo when the user clicks the button.
- Remove a todo when the user clicks the X button.
To do the first step we have to use a new hook:
In our case, we need the reference to the input element, so that when we click the "+" button, we can access the value property of the input element, and add a new todo only if the value is not empty. After we add it, we should also empty the input.
We can also already implement the todo removal functionality. Big step we ar going to do:
// ...
export default function TodoList() {
const [todos, setTodos] = useState(initialTodos);
const inputRef = useRef();
const addTodo = () => {
const input = inputRef.current;
const newTodo = input.value;
if (newTodo.trim()) {
setTodos([...todos, newTodo]);
input.value = '';
}
};
const removeTodo = (index) => {
setTodos(todos.filter((todo, i) => i !== index));
};
return html`
<div>
<div>
<input ref=${inputRef} />
<button @click=${addTodo}>+</button>
</div>
<ul>
${todos.map(
(todo, i) =>
html`<li>
<button @click=${() => removeTodo(i)}>X</button>
<span>${todo}</span>
</li>`,
)}
</ul>
</div>
`;
}
// ...We are so close to finish!
The only thing that is left is saving the todos so that when the user comes back to the page they are not lost. To do that we have to somehow know when the component is first rendered, get the todos from the localStorage, and render them. We also have to modify the saved todos whenever the user adds or deletes one of them. To implement this functionality the
export default function TodoList() {
// ...
// load initial todos
useEffect(() => {
const savedTodos = localStorage.getItem('todos');
if (savedTodos) setTodos(JSON.parse(savedTodos));
}, []);
// save todos
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
return html`...`;
}If you use this component multiple times, it'll always render the same items, because they are picked from the same localStorage key. You are free to further modify the component so that the items will be picked from a unique localStorage key.
Here's the result:
Be proud of yourself!
Quick Start
Create a Wompo component with props, events, useState, conditions, and lists.
Styling your components
Explore different approaches to style your components: basic CSS, with Shadow DOM, or with Wompo's built-in CSS Modules.