Hey folks,
I am pretty excited to share this with you all. It is about how you can have better typing and intelisense in your ReactJS app. So if you wanna utilize TS features to the maximum sit tight.
Problem
So I was reading ReactJS doc and I saw this example So I decided to implement it in ReactJS + CSS + Typescript. No third party lib or CSS framework. And the result is super duper beautiful if you ask me.
So here is the catch about how I wanted to use useReducer
hook:
- I had my reducer defined like this:
function reducer(tasks: Task[], action: TaskManagerAction): Task[] {
if (action.type === 'create') {
return [
...tasks,
{
id: Math.random().toString(),
name: action.taskName,
inProgress: false,
},
];
}
if (action.type === 'update') {
return tasks.map((task) => {
if (task.id === action.taskId) {
return {
id: task.id,
name: action.taskName,
inProgress: action.taskInProgress,
};
}
return task;
});
}
if (action.type === 'delete') {
return tasks.filter((task) => task.id !== action.taskId);
}
throw 'invalid action type!';
}
- And firstly I wrote my
TaskManagerAction
like this:
interface TaskManagerAction {
type: 'update' | 'create' | 'delete';
taskId: string;
taskName: string;
taskInProgress: boolean;
}
- Then I tried to use it as my reducer function in useReducer:
const [tasks, dispatch] = useReducer(reducer, []);
- But soon I've realized that when I am gonna use
dispatch
method to invoke one of those actions I have to either make all three other keys optional or pass them. It does not matter that I did not need them, but I had to because they were defined as required.
And making them all optional was not a good choice either because it defeats the purpose of using TS.
Solution
Then I decided to poke around an realized that TS has this awesome feature where it can narrow down the type based on the action.type
that we're passing to the dispatch method.
So it is a life saver, let's look at it:
interface CreateTaskManagerAction {
type: 'create';
taskName: string;
}
interface UpdateTaskManagerAction {
type: 'update';
taskId: string;
taskName: string;
taskInProgress: boolean;
}
interface DeleteTaskManagerAction {
type: 'delete';
taskId: string;
}
type TaskManagerAction =
| CreateTaskManagerAction
| UpdateTaskManagerAction
| DeleteTaskManagerAction;
As you can see I am telling TS that TaskManagerAction is either one of those three other types and they have all different key-value pairs. and inside the code you can see that VSCode is deriving the type of action from action.type
:
And You can find my code here. Go to src/components/task-manager
:
I also found this YouTube video in which he is explaining another use case of discriminated unions in ReactJS: