useRef
useRef에 값과 같은 데이터가 아닌 input을 담을 때는 HTMLInputElement 타입을 지정해야한다.
const titleRef = useRef<HTMLInputElement>(null);
const contentRef = useRef<HTMLInputElement>(null);
form submit
form submit의 매개변수 event 를 사용하기 위해서는 e: React.FormEvent<HTMLFormElement> 타입을 지정해야 한다.
const form = e.target as HTMLFormElement;
todo 추가 완료 후 form reset 하기 위해 선언.
titleRef.current?.focus();
titleRef 와 contentRef 의 초기값이 null 이기 때문에 옵셔널 체이닝을 사용하여 에러 방지.
const titleRef = useRef<HTMLInputElement>(null);
const contentRef = useRef<HTMLInputElement>(null);
//todo 추가
const submitHandler = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const target = e.target as typeof e.target & { //'as' 타입 단언 - e.target의 타입이면서 title과 content 속성을 가진 타입임을 명시.
title: { value: string };
content: { value: string };
};
if (!target.title.value.trim()) {
alert("제목을 입력해주세요.");
titleRef.current?.focus(); //옵셔널 체이닝 - 초기값이 null 이기 때문에 옵셔널 체이닝 안하면 에러남.
return;
}
if (!target.content.value.trim()) {
alert("내용을 입력해주세요.");
contentRef.current?.focus();
return;
}
const newTodo = {
title: target.title.value,
content: target.content.value,
isDone: false
};
mutation.mutate(newTodo);
form.reset();
};
TodoForm.jsx 전체 코드
import { addTodo } from "api/todos";
import React, { useRef } from "react";
import { useMutation, useQueryClient } from "react-query";
import { FormContainer, InputStyle } from "./TodoFormStyle";
import { BtnBlackF } from "./TodoListStyle";
export const TodoForm = () => {
const queryClient = useQueryClient();
const mutation = useMutation(addTodo, {
onSuccess: () => {
queryClient.invalidateQueries("todos");
}
});
const titleRef = useRef<HTMLInputElement>(null);
const contentRef = useRef<HTMLInputElement>(null);
//todo 추가
const submitHandler = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const target = e.target as typeof e.target & {
title: { value: string };
content: { value: string };
};
if (!target.title.value.trim()) {
alert("제목을 입력해주세요.");
titleRef.current?.focus();
return;
}
if (!target.content.value.trim()) {
alert("내용을 입력해주세요.");
contentRef.current?.focus();
return;
}
const newTodo = {
title: target.title.value,
content: target.content.value,
isDone: false
};
mutation.mutate(newTodo);
form.reset();
};
return (
<FormContainer onSubmit={submitHandler}>
<label htmlFor="title">제목</label>
<InputStyle type="text" id="title" name="title" ref={titleRef} placeholder="제목을 입력해주세요." />
<label htmlFor="content">내용</label>
<InputStyle type="text" id="content" name="content" ref={contentRef} placeholder="내용을 입력해주세요." />
<BtnBlackF>추가</BtnBlackF>
</FormContainer>
);
};
react-query
api/todos.ts 전체 코드
import axios from "axios";
//todo 추가
export const addTodo = async (newTodo: { title: string; content: string; isDone: boolean }) => {
try {
return await axios.post(`${process.env.REACT_APP_JSON_SERVER_URL}/todos`, newTodo);
} catch (error) {
alert("에러가 발생했습니다. 관리자에게 문의해주세요.");
console.error(error);
}
};
type Todo = {
id: string;
title: string;
content: string;
isDone: boolean;
};
//todo 조회
export const getTodos = async (): Promise<Todo[] | undefined> => {
try {
const { data } = await axios.get(`${process.env.REACT_APP_JSON_SERVER_URL}/todos`);
return data;
} catch (error) {
alert("에러가 발생했습니다. 관리자에게 문의해주세요.");
console.error(error);
}
};
//todo 완료 여부 변경
export const toggleTodoDone = async ({ id, isDone }: { id: string; isDone: boolean }) => {
try {
return await axios.patch(`${process.env.REACT_APP_JSON_SERVER_URL}/todos/${id}`, { isDone });
} catch (error) {
alert("에러가 발생했습니다. 관리자에게 문의해주세요.");
console.error(error);
}
};
//todo 삭제
export const deleteTodo = async (id: string) => {
try {
return await axios.delete(`${process.env.REACT_APP_JSON_SERVER_URL}/todos/${id}`);
} catch (error) {
alert("에러가 발생했습니다. 관리자에게 문의해주세요.");
console.error(error);
}
};
이번에 처음 typescript 를 사용하면서 다양한 부분에 타입을 지정하게 되었는데, 패턴?을 기억하고자 남기기로 했다.
처음에는 얼마나 많은 에러가 나던지...
매개변수 부분의 타입 선언은 필수이고, 함수 리턴 부분의 타입은 지정하는게 맞다, 추론해주니까 안해도 된다의 두가지 의견이 분분하다고 한다.
좀 더 공부해서 어떤게 맞는지 생각해봐야 할 것 같다. 지금은 무리다...
유틸리티 타입도 사용해보고 싶었지만 아직 공부가 부족한 탓인지 사용할 수 없었다...
'프로젝트 > 2024' 카테고리의 다른 글
최종 팀 프로젝트 Day2 (0) | 2024.03.27 |
---|---|
최종 팀 프로젝트 시작! (0) | 2024.03.26 |
로그인 기능 작업을 하면서 (0) | 2024.02.21 |
crypto.randomUUID 적용 (0) | 2024.01.23 |
[javascript] 리뷰 삭제 (localStorage) (0) | 2024.01.15 |