Console Error : A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://react.dev/link/controlled-components
➡️ 즉, value 또는 checked 속성을 사용하면서 상태값을 제대로 초기화하지 않거나, 상태값이 없을 때 undefined가 전달된 경우 위의 에러가 발생한다.
근데 이제 controlled component와 uncontrolled component 중 하나를 선택할 때, 당장의 에러 해결뿐 아니라 장기적인 값의 활용까지 고려해야 해서 고민이다 이거죠🥹
수정전
const LabeledInput: React.FC<LabeledInputProps> = ({ id, name, label, type = "text", placeholder, required = true }) => {
return (
<>
<div>
<label htmlFor="{id}">{label}</label>
<input id={id} name={name} type={type} placeholder={placeholder} required={required} />
</div>
</>
);
};
수정후
const LabeledInput = React.forwardRef<HTMLInputElement, LabeledInputProps>(
({ id, name, label, type = "text", placeholder, required = true }, ref) => {
return (
<div>
<label htmlFor={id}>{label}</label>
<input id={id} name={name} type={type} placeholder={placeholder} required={required} ref={ref} />
</div>
);
}
);
수정전
const MeetupForm = () => {
const queryClient = useQueryClient();
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzMyMTg3MjE4LCJpYXQiOjE3MzIxODY5MTgsImp0aSI6ImVjZDc0ZDBiOGEyODRmODZhMGE3MjRmYjUzNTBkNmRkIiwidXNlcl9pZCI6Mn0.Oy6DifyBzOdwsQt3kGxKEicCockgcsCuWlWDAEnBFG0";
const [isPublicChecked, setIsPublicChecked] = useState(true);
const [isStartedAtChecked, setIsStartedAtChecked] = useState(false);
const [isEndedAtChecked, setIsEndedAtChecked] = useState(false);
const [isStartedAtNullChecked, setIsStartedAtNullChecked] = useState(false);
const [isEndedAtNullChecked, setIsEndedAtNullChecked] = useState(false);
const createMutation = useMutation<void, Error, Meetup>({
mutationFn: createMeetup,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["meetups"] });
},
});
const handleMeetupFormSubmit = (event: React.FormEvent) => {
event.preventDefault();
// const form = event.currentTarget as HTMLFormElement;
const nameValue = (event.target as HTMLInputElement).value;
const descriptionValue = (event.target as HTMLInputElement).value;
const placeDescriptionValue = (event.target as HTMLInputElement).value;
const placeValue = (event.target as HTMLInputElement).value;
const startedAtValue = (event.target as HTMLInputElement).value;
const endedAtValue = (event.target as HTMLInputElement).value;
const adTitleValue = (event.target as HTMLInputElement).value;
const adEndedAtValue = (event.target as HTMLInputElement).value;
// const isPublicValue = (form.elements.namedItem("isPublic") as HTMLInputElement).checked;
const imageValue = (event.target as HTMLInputElement).value;
const categoryValue = (event.target as HTMLInputElement).value;
const newMeetup = {
// name: form.name.value,
name: nameValue,
description: descriptionValue,
place: placeValue,
placeDescription: placeDescriptionValue,
startedAt: startedAtValue,
endedAt: endedAtValue,
adTitle: adTitleValue,
adEndedAt: adEndedAtValue,
// isPublic: isPublicValue,
image: imageValue,
category: categoryValue,
};
createMutation.mutate(newMeetup);
// event.target.reset();
};
return (
<>
<div>
<form onSubmit={handleMeetupFormSubmit}>
<div>
{/* <LabeledInput id="category" name="category" label="모임 성격" type="text" required /> */}
<select>
<option value="">{value}</option>
</select>
<LabeledInput id="title" name="title" label="모임 이름(랜덤 생성 버튼 필요)" type="text" required />
<LabeledInput id="startedAt" name="startedAt" label="모임 시작 날짜" type="date" required />
<LabeledInput id="endedAt" name="endedAt" label="모임 종료 날짜" type="date" required />
<LabeledInput id="place" name="place" label="모임 지역" type="text" required />
<LabeledInput id="placeDescription" name="placeDescription" label="모임 장소" type="text" placeholder="만날 곳의 대략적 위치를 적어주세요. 예) 강남역" required />
<LabeledInput id="adTitle" name="광고글 제목" label="광고글 제목" type="text" minlength="2" maxlength="5" required />
<LabeledInput id="adEndedAt" name="adEndedAt" label="광고 종료 날짜" type="date" required />
</div>
<div>
<label htmlFor="description">광고글 설명</label>
<textarea id="description" name="description" defaultValue=""></textarea>
</div>
<div>
<label htmlFor="isPublic">광고글 공개하기</label>
<input id="isPublic" type="checkbox" checked={isPublicChecked} onChange={() => setIsPublicChecked(!isPublicChecked)} />
</div>
<div>
<button type="submit">모임 생성하기</button>
</div>
</form>
</div>
);
};
수정후
const MeetupForm = () => {
const adTitleRef = useRef<HTMLInputElement>(null);
const handleMeetupFormSubmit = (event: React.FormEvent) => {
event.preventDefault();
const adTitle = adTitleRef.current?.value || "";
console.log("Submitted adTitle:", adTitle);
};
return (
<form onSubmit={handleMeetupFormSubmit}>
<LabeledInput id="adTitle" name="adTitle" label="광고글 제목" ref={adTitleRef} required />
<button type="submit">모임 생성하기</button>
</form>
);
};
https://velog.io/@juno7803/React-useRef-200-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0