Custom Hook 來自己寫一個 hook 吧!


Posted by 半夏 on 2021-08-25

首先, 自訂的 Hook 一定要用 use 作為開頭

會用到的 hook 記得要 import 進來。
把變數 return 回去就可以在 app.js 中使用。

新增一個檔案叫做 useInput.js 把 App.js 裡面的邏輯抽出來

useInput.js

import { useState } from "react";

export default function useInput() {
  const [value, setValue] = useState("");
  const handleChange = (e) => {
    setValue(e.target.value);
  };

  return {
    value,
    setValue,
    handleChange,
  };
}

再做一個把 todos 會用到的東西抽出來

useTodos.js

import { useState, useEffect, useRef } from "react";
import useInput from "./useInput";

function writeTodosToLocalStorage(todos) {
  window.localStorage.setItem("todos", JSON.stringify(todos));
}

export default function useTodos() {
  const id = useRef(1);
  const { value, setValue, handleChange } = useInput();
  const [todos, setTodos] = useState(() => {
    let todoData = window.localStorage.getItem("todos") || "";
    if (todoData) {
      todoData = JSON.parse(todoData);
      id.current = todoData[0].id + 1;
    } else {
      todoData = [];
    }
    return todoData;
  });

  useEffect(() => {
    writeTodosToLocalStorage(todos);
  }, [todos]);

  const handleButtonClick = () => {
    setTodos([
      {
        id: id.current,
        content: value,
        size: "XL",
      },
      ...todos,
    ]);
    setValue("");
    id.current++;
  };

  const handleToggleIsDone = (id) => {
    setTodos(
      todos.map((todo) => {
        if (todo.id !== id) return todo; // 不等於要修改的 id 直接回傳
        return {
          ...todo,
          isDone: !todo.isDone,
        }; // 是要修改的 id 就把原本的 todo 加上要修改的屬性
      })
    );
  };

  const handleDeleteTodo = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  return {
    todos,
    setTodos,
    id,
    handleButtonClick,
    handleToggleIsDone,
    handleDeleteTodo,
    value,
    setValue,
    handleChange,
  };
}

如果要用同一個 hook 在新的 input 裡面使用,這樣寫就可以調用了
變成 ES6 object 的寫法

// 假如有第二個 input 也需要這個方法,就可以用:
const { value: todoName, setValue: setTodoName, handleChange: handleTodoName } = useInput();

最後 App.js 檔案就變得很乾淨了
import { useState, useRef, useEffect } from "react";
也都不用寫在 App.js 裡面

App.js

import TodoItem from "./TodoItem";
import useTodos from "./useTodos";

function App() {
  const {
    todos,
    setTodos,
    id,
    handleButtonClick,
    handleToggleIsDone,
    handleDeleteTodo,
    value,
    setValue,
    handleChange,
  } = useTodos();

  return (
    <div className="App">
      <input type="text" value={value} onChange={handleChange} />
      <button onClick={handleButtonClick}>Add todo</button>
      {todos.map((todo) => (
        <TodoItem
          key={todo.id}
          todo={todo}
          title={todo.title}
          size={todo.size}
          handleDeleteTodo={handleDeleteTodo}
          handleToggleIsDone={handleToggleIsDone}
        />
      ))}
    </div>
  );
}

export default App;

這樣拆開來寫的好處是,介面跟邏輯分開,把共同的邏輯抽出來另外寫,這樣以後只要在套用一樣的邏輯,就可以產出不一樣的 UI 畫面了,是不是很棒呢~^^


#React #custom hook







Related Posts

氣泡排序(Bubble Sort)、插入排序(Insertion Sort)、選擇排序(Selection Sort)

氣泡排序(Bubble Sort)、插入排序(Insertion Sort)、選擇排序(Selection Sort)

製作 Figma Plugin

製作 Figma Plugin

How to Launch an Amazon EC2 Instance

How to Launch an Amazon EC2 Instance


Comments