Beginner TypeScript: Vanilla&React Comparison
Tags: typescript, react, beginners, javascript
Published: 2023-07-17 04:12:54



I started TypeScript as many YouTubers and companies say it is in demand these days.

Since I can understand English and Japanese, I used resources from both languages. 
So, in this post, I make a comparison to embark on a new journey of TypeScript as an absolute beginner🔰🛳️


##Videos

I used two YouTube videos, the first is WebDevSimplified.
{% embed https://www.youtube.com/watch?v=jBmrduvKl5w %}






The second is プログラミングチュートリアル.
{% embed https://www.youtube.com/watch?v=ANcopd8Bmao&t=666s %}




Although they have different approaches, they have the same "To-do List" project.
So I tried if I could understand their differences and functionality.



I really struggled to understand them by placing them on screen.
The left is ①WebDevSimplified and the right is ②プログラミングチュートリアル.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wd9xxeg7j1vl98d0hcj1.png)





##Source code

①　https://github.com/WebDevSimplified/typescript-todo-list/tree/main

```
import { v4 as uuidV4 } from "uuid"

type Task = {
  id: string
  title: string
  completed: boolean
  createdAt: Date
}

const list = document.querySelector<HTMLUListElement>("#list")
const form = document.getElementById("new-task-form") as HTMLFormElement | null
const input = document.querySelector<HTMLInputElement>("#new-task-title")
const tasks: Task[] = loadTasks()
tasks.forEach(addListItem)

form?.addEventListener("submit", e => {
  e.preventDefault()

  if (input?.value == "" || input?.value == null) return

  const newTask: Task = {
    id: uuidV4(),
    title: input.value,
    completed: false,
    createdAt: new Date(),
  }
  tasks.push(newTask)
  saveTasks()

  addListItem(newTask)
  input.value = ""
})

function addListItem(task: Task) {
  const item = document.createElement("li")
  const label = document.createElement("label")
  const checkbox = document.createElement("input")
  checkbox.addEventListener("change", () => {
    task.completed = checkbox.checked
    saveTasks()
  })
  checkbox.type = "checkbox"
  checkbox.checked = task.completed
  label.append(checkbox, task.title)
  item.append(label)
  list?.append(item)
}

function saveTasks() {
  localStorage.setItem("TASKS", JSON.stringify(tasks))
}

function loadTasks(): Task[] {
  const taskJSON = localStorage.getItem("TASKS")
  if (taskJSON == null) return []
  return JSON.parse(taskJSON)
}

```



②　https://github.com/Shin-sibainu/todolist-react-typescript

```
import React, { useState } from "react";
import "./App.css";

function App() {
  const [inputText, setInputText] = useState("");
  //todo配列オブジェクトの更新用に用意。プロパティはinputValue, id, checkedの３つを更新する。
  const [todos, setTodos] = useState<Todo[]>([]);

  type Todo = {
    inputValue: string;
    id: number; //keyを指定するため
    checked: boolean;
  };

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    e.preventDefault();

    setInputText(e.target.value);
  };

  const handleSubmit = (e: { preventDefault: () => void }) => {
    e.preventDefault();

    if (!inputText) {
      return;
    }

    //新しいTodo作成
    const newTodo: Todo = {
      inputValue: inputText,
      id: todos.length,
      checked: false,
    };

    setTodos([newTodo, ...todos]);
    console.log(inputText);
    setInputText(inputText);
  };

  //todo編集　　　　　🤔①
  const handleEdit = (id: number, inputValue: string) => {
    /* ディープコピー(完全にコピーされた別の配列)に変えよう(ミューテートから守るため) */　　　　🤔②
    const deepCopy = todos.map((todo) => ({ ...todo }));
    console.log(deepCopy);
　　　　　　🤔③
    const newTodos = deepCopy.map((todo) => {
      if (todo.id === id) {
        todo.inputValue = inputValue;
      }
      return todo;
    });

    // /* シャローコピー */
    // const newTodos = todos.map((todo) => {
    //   if (todo.id === id) {
    //     todo.inputValue = inputValue;
    //   }
    //   return todo;
    // });

    setTodos(newTodos);
  };

  //完了未完了 
  const handleChecked = (id: number, checked: boolean) => {
    /* ディープコピー(完全にコピーされた別の配列)に変えよう(ミューテートから守るため) */
    const deepCopy = todos.map((todo) => ({ ...todo }));
    // console.log(deepCopy);

    const newTodos = deepCopy.map((todo) => {
      if (todo.id === id) {
        //反転
        todo.checked = !checked;
      }
      return todo;
    });

    setTodos(newTodos);
  };

  //削除
  const handleDelete = (id: number) => {
    //idが正しくないのは残す。正しいと消す。
    const newTodos = todos.filter((todo) => todo.id !== id);
    setTodos(newTodos);
  };

  return (
    <div className="App">
      <div>
        <h2>Todoリスト with Typescript</h2>
        <form onSubmit={(e) => handleSubmit(e)}>
          <input
            type="text"
            onChange={(e) => handleChange(e)}
            className="inputText"
          />
          <input type="submit" value="作成" className="submitButton" />
        </form>
        {/* タスク設定が完了したら */}
        <ul className="todoList">
          {todos.map((todo) => (
            <li key={todo.id}>
              <input
                type="text"
                value={todo.inputValue}
                onChange={(e) => handleEdit(todo.id, e.target.value)}
                disabled={todo.checked}
              />
              <input
                type="checkbox"
                checked={todo.checked}
                onChange={() => handleChecked(todo.id, todo.checked)}
              />
              <button onClick={() => handleDelete(todo.id)}>消</button>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

export default App;
```

## Thx ChatGPT!!!😭😭

I got ChatGPT to make a table to compare them.



| Aspect                      | Folder 1 (WebDevSimplified) | Folder 2 (React App) |
|-----------------------------|------------------------------|----------------------|
| **Language**                | JavaScript/TypeScript        | TypeScript           |
| **UI Library/Framework**    | None                         | React                |
| **State Management**        | Local variables and local storage | React `useState` hook |
| **Type Definition**         | The `Task` type is defined with a type alias and used immediately. | The `Todo` type is defined with a type alias inside the component function. |
| **Form Handling**           | Uses traditional form and input handling with DOM APIs. | Uses React's controlled components pattern with `useState`. |
| **Task/Todo Creation**      | Creates a new task object and pushes it to the tasks array. | Creates a new todo object and uses the `setTodos` function to update state. |
| **Task/Todo Editing**       | Checkbox listener directly modifies the task's `completed` property. | Uses a `handleEdit` function that creates a new array with the edited todo. |
| **Task/Todo Completion**    | Checkbox listener directly modifies the task's `completed` property and saves to local storage. | Uses a `handleChecked` function that creates a new array with the toggled `checked` property of the todo. |
| **Task/Todo Deletion**      | Not implemented.             | Uses a `handleDelete` function that filters the todos array to exclude the deleted one. |
| **Task/Todo Rendering**     | Uses the `addListItem` function to create and append HTML elements. | Uses JSX to map over the todos array and render list items. |
| **Data Persistence**        | Uses `localStorage` to persist tasks. | Does not persist todos beyond the current session. |


## Where I didn't understand


### 🤔① What is “`?`” and why there’s nothing after `return` ?

On line 19 of WebDevSimplified,
`if (input?.value == "" || input?.value == null) return`

#### As for `?`
**🙋‍♂️WebDevSimplified says**

> that's because when we do our query selector for our input right here
>  it's returning to us something that is either an html input element or something that is null.
> Because what if nothing with this id exists? 
> Well, we need to take that into account typescript says hey this thing could be null so you need to take into account that it could be null.

But I couldn't get it well. So I asked ChatGPT.


**🌸ChatGPT says**

>  Optional chaining: The video introduces optional chaining (?.) as a way to handle the possibility of null. Optional chaining is a way to tell TypeScript: "Hey, try to access this property or method, but if the object is null or undefined, just give me undefined immediately and don't throw an error." This is what the input?.value does in your code.

#### As for `return`

**🌸ChatGPT says**

> The line `if (input?.value == "" || input?.value == null) return` is using what's known as an "early return" or a "guard clause". This is a common pattern in programming to stop the execution of a function if certain conditions are met.
> In this case, the function is stopped (i.e., it "returns") if the value of `input` is either `null`, `undefined`, or an empty string (`""`). This is to prevent the creation of a new task when the input is empty or invalid.


🙋‍♀️So to summarize with my words, since TypeScript is more strict than normal js, we can let TypeScript think there is a possibility that the input might be `null` or `undefined`. If it is the case, TypeScript should return `undefined` immediately and do nothing after that.




### 🤔② Which `id` relates to which `id` in プログラミングチュートリアル?



On around line 41 of プログラミングチュートリアル,
```
  const handleEdit = (id: number, inputValue: string) => {
    /* ディープコピー(完全にコピーされた別の配列)に変えよう(ミューテートから守るため) */
    const deepCopy = todos.map((todo) => ({ ...todo }));
    console.log(deepCopy);

    const newTodos = deepCopy.map((todo) => {
      if (todo.id === id) {
        todo.inputValue = inputValue;
      }
      return todo;
    });



~~~~~~

              <input
                type="text"
                value={todo.inputValue}
                onChange={(e) => handleEdit(todo.id, e.target.value)}
                disabled={todo.checked}
              />

```

I got confused how to understand which `id` corresponds to which.😂So I asked ChatGPT to put emojis.


```
// Define the Todo type
type Todo = {
  inputValue: string;
  id: number; // This id is the original source of both 🦥id and 🐼id.
  checked: boolean;
};

// Define the handleEdit function
const handleEdit = (🦥id: number, inputValue: string) => {
  const deepCopy = todos.map((todo) => ({ ...todo }));

  const newTodos = deepCopy.map((todo) => {
    if (todo.🐼id === 🦥id) { // Compare the 'id' property of a Todo object to the 'id' argument of the function
      todo.inputValue = inputValue;
    }
    return todo;
  });

  setTodos(newTodos);
};

// Call the handleEdit function in the JSX
<input
  type="text"
  value={todo.inputValue}
  onChange={(e) => handleEdit(todo.🦥id, e.target.value)} // Pass the 'id' property of the current Todo object as the first argument to the function
  disabled={todo.checked}
/>
```

**🌸ChatGPT says**

> - 🦥id: This is the argument to the **`handleEdit`** function. When the **`handleEdit`** function is called in the **`onChange`** event handler, the **`id`** property of the current **`Todo`** object is passed as this argument.
> - 🐼id: This is the **`id`** property of a **`Todo`** object. In the **`handleEdit`** function, this **`id`** is compared with the **`🦥id`** argument to find the **`Todo`** object that needs to be edited.

> So, when **`handleEdit`** is called with **`todo.🦥id`** and **`e.target.value`**, it maps over the **`todos`** array to create a new array. For each **`Todo`** object in the new array, if the **`id`** property of the **`Todo`** object (**`todo.🐼id`**) matches the **`🦥id`** argument, it updates the **`inputValue`** property of the **`Todo`** object to **`e.target.value`**.



### 🤔③ What is deep / shallow copy in プログラミングチュートリアル?


Since it was the first time, I didn't understand general concepts in programming.


**🌸ChatGPT says**

> - **Shallow copy**: When you create a shallow copy of an object, you're creating a new object with the same properties, **but** the property values are **not duplicated.** This means that if a property value is an object or an array, the copy and the original will share the same reference to that object or array. If you modify the object or array through one of the references, the change will be visible when you access it through the other reference.
> - **Deep copy**: When you create a deep copy of an object, you're creating a new object with the same properties, and **all the property values are also duplicated.** This means that the copy and the original have completely **separate copies** of all data, including nested objects and arrays. Changes to one will **not affect the other.**




### 🤔④ Why do we need to make `const newTodos ` in プログラミングチュートリアル?
**🌸ChatGPT says**

 

> The primary reason for creating `newTodos` is to maintain the immutability of React's state.
> In React, you should not directly modify state. Instead of altering or updating an existing state object directly, you should generate a new state object and set that. This is a crucial part of how React detects changes to state and re-renders appropriately.
> For instance, when you want to change the `inputValue` of a `Todo`, instead of directly modifying it like this:
```
// Don't do this!
todos[0].inputValue = "new value";
```

> You should create a new array of `Todo`s, create a new `Todo` object with the changes, and then set that as the state:
```
const newTodos = todos.map((todo) => {
  if (todo.id === idToEdit) {
    return { ...todo, inputValue: "new value" };
  } else {
    return todo;
  }
});
setTodos(newTodos);
```

> So, `newTodos` is used to create a new array with the changes applied, without directly modifying the original `todos` array. This maintains the immutability of React's state, allowing React to properly detect changes to state and re-render.


## Thank you for reading!

I had a lot to learn, not only TypeScript, but also React and JavaScript concepts. I am never confident that I perfectly understand each code, but I'm happy that I made a progress🏋️🏋️‍♀️🏋️‍♂️