The basic UI
The app UI contains:
- A title
- An input to add more todo items
- A list of todos
- A button to remove all todo items
- React
- React Native
function AppContent() {
return (
<div>
<h1>Todo List</h1>
<TodoInput />
<ListOfTodos />
<RemoveAllButton />
</div>
);
};
function AppContent() {
return (
<View>
<Text>Todo List</Text>
<TodoInput />
<ListOfTodos />
<RemoveAllButton />
</View>
);
};
Let's detail below the components: TodoInput
, ListOfTodos
and RemoveAllButton
.
TodoInput
The TodoInput
component is a simple input field that allows the user to type a new todo item,
and then press Enter
or click a button to add it to the list.
- React
- React Native
function TodoInput() {
const [inputText, setInputText] = useState<string>('');
const store = useStore();
async function processInput(text: string) {
let status = await store.dispatchAndWait(new AddTodoAction(text))
if (status.isCompletedOk) setInputText('');
}
return (
<div>
<input
placeholder="Type here..."
value={inputText} maxLength={50}
onChange={(e) => { setInputText(e.target.value); }}
onKeyDown={(e) => { if (e.key === "Enter") processInput(inputText); }}
/>
<button onClick={() => processInput(inputText)}>
Add
</button>
</div>
);
};
function TodoInput() {
const [inputText, setInputText] = useState<string>('');
const store = useStore();
async function processInput(text: string) {
let status = await store.dispatchAndWait(new AddTodoAction(text));
if (status.isCompletedOk) setInputText('');
}
return (
<View>
<TextInput
placeholder={'Type here...'}
value={inputText}
onChangeText={(text) => { setInputText(text); }}
onSubmitEditing={() => processInput(inputText)}
/>
<TouchableOpacity onPress={() => processInput(inputText)}>
<Text>Add</Text>
</TouchableOpacity>
</View>
);
};
As you can see above, the processInput
function is called whenever the user presses Enter
or clicks the "Add" button. This function uses the useStore
hook to get a reference to the store,
and then dispatches an AddTodoAction
with the input text.
A simplified version of the processInput
could simply use the store's dispatch
method:
async function processInput(text: string) {
store.dispatch(new AddTodoAction(text));
}
However, when the action succeeds, we want to clear the input field. To do this, we need
to wait until the action is completed, check if it completed successfully, and then clear
the input field with setInputText('')
.
This is why instead of dispatch
we want to use the dispatchAndWait
method.
It returns a Promise
that completes when the action is completed.
If we await it we get a status of type ActionStatus
that tells us if the action was successful or not.
In other words, we get the status
,
and if status.isCompletedOk
is true we can clear the input field:
async function processInput(text: string) {
let status = await store.dispatchAndWait(new AddTodoAction(text));
if (status.isCompletedOk) setInputText('');
}
ListOfTodos
The list of todos uses the useSelect
hook to get the list of todos from state.todoList.items
,
and then maps over them to render each todo item component.
function ListOfTodos() {
const todoItems = useSelect((state) => state.todoList.items);
return (
<div className="listOfTodos">
{todoItems.map((item, index) => (
<TodoItemComponent key={index} item={item} />
))}
</div>
);
}
TodoItemComponent
For the moment, the todo item component is just the text of the item.
function TodoItemComponent({item}: {item: TodoItem}) {
return <div>{item.text}</div>;
}
RemoveAllButton
Finally, we add a button that uses the useStore
hook to get a reference to the store,
and then uses it to dispatch a RemoveAllTodosAction
when clicked.
function RemoveAllButton() {
const store = useStore();
return (
<button onClick={() => store.dispatch(new RemoveAllTodosAction())}>
Remove All
</button>
);
}
Try it yourself
Type "Buy milk" in the input, and press the Add
button or the Enter
key.
Try adding other todo items.
Then remove all of them by clicking the Remove All
button.
To see all files in this project,
click the "hamburger icon" in the top left corner of the code editor.
The state classes are in file State.ts
,
while the store, actions and components are in the App.tsx
file.