TIL #46 | Todolist를 typescript로 리팩토링하기

kibi·2023년 12월 18일
1

TIL (Today I Learned)

목록 보기
46/83

RTK & thunk를 사용한 리팩토링

1) useAppDispatch & useAppSelector

typescript로 useDispatch와 useSelector를 사용하기 위해 타입을 지정해주어야 한다.
먼저 store에서 각각 필요한 타입을 가져온다.

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

hooks > redux 파일에 생성한 타입을 각각 지정해준다.

import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "../store";

export const useAppDispatch = () => useDispatch<AppDispatch>();
// TypedUseSelectorHook를 사용하여 type 지정
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

사용할 때 useDispatch와 useSelector가 아닌, 타입을 지정한 useAppDispatch와 useAppSelector로 사용해준다.

const dispatch = useAppDispatch();
const { todos, isLoading, error } = useAppSelector((state) => state.todosSlice);

2) Reducer

  • initialState type 지정
interface TodosType {
  todos: Todo[];
  editTitle: string;
  isLoading: boolean;
  error: unknown;
}

const initialState: TodosType = {
  todos: [] as Todo[],
  editTitle: "",
  isLoading: true,
  error: null,
};
  • 비동기로 가져올 데이터의 타입을 지정해준다.
const getTodosFromDB = async () => {
  // <Todo[]>로 타입 지정
  const { data } = await axios.get<Todo[]>(
    `${process.env.REACT_APP_SERVER_URL}/todos`
  );
  return data;
};

export const __getTodos = createAsyncThunk(
  "todos/getTodos",
  async (_, thunkAPI) => {
    try {
      const todos = await getTodosFromDB();
      return todos;
    } catch (err) {
      return thunkAPI.rejectWithValue(err);
    }
  }
);
  • extraReducers 구현
  extraReducers: (builder) => {
    builder
      .addCase(__getTodos.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(__getTodos.fulfilled, (state, action) => {
        state.isLoading = false;
        state.todos = action.payload;
      })
      .addCase(__getTodos.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload as string; // 타입 단언
      })



RTK & useQuery를 사용한 리팩토링

1) api 구성

import axios from "axios";
import { Todo } from "../types/todo";

// Promise<Todo[]>로 타입 정의 
const getTodos = async (): Promise<Todo[]> => {
  const { data } = await axios.get("http://localhost:4000/todos");
  console.log(data);
  return data;
};

// 매개변수의 타입과 리턴의 타입을 지정
// return이 없으므로 Promise<void>로 지정
const addTodo = async (newTodo: Todo): Promise<void> => {
  await axios.post("http://localhost:4000/todos", newTodo);
};

const deleteTodo = async (id: string): Promise<void> => {
  await axios.delete(`http://localhost:4000/todos/${id}`);
};

const toggleIsDoneTodo = async (payload: Todo): Promise<void> => {
  console.log(payload);
  await axios.patch(`http://localhost:4000/todos/${payload.id}`, {
    isDone: !payload.isDone,
  });
};

export { getTodos, addTodo, deleteTodo, toggleIsDoneTodo };

2) useQuery와 useMutation을 사용한 CRUD 구현

(Javascript를 사용한 경우의 useQuery 사용방법)

  const { isLoading, isError, data } = useQuery("todos", getTodos, {
    retry: 5,
  });
  const deleteMutation = useMutation(deleteTodo, {
    onSuccess: () => {
      queryClient.invalidateQueries("todos");
    },
  });

typescript를 사용할 경우의 useQuery 및 useMutation 사용방법

  • queryKey, queryFn, mutationFn 등을 명시해주어야 한다.
  // useQuery
  const {
    isLoading,
    error,
    data: todos,
  } = useQuery<Todo[], AxiosError<unknown, any>, Todo[], string[]>({
    queryKey: ["todos"],
    queryFn: getTodos,
    retry: 5,
  });
  // Mutations
  const deleteMutation = useMutation({
    mutationFn: deleteTodo,
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({ queryKey: ["todos"] });
    },
    onError: (error: string) => {
      console.error(error);
    },
  });

0개의 댓글