상황: 어드민페이지를 만드는 중이었다.
이미지 추가하여 미리보여주는 컴포넌트는 한개이며, 아래와 같다.
state를 컴포넌트 내에서 각각 관리하게 하여
이미지를 업로드 할 때도 각각 보일 수 있도록 했었다.
import { useState, useRef } from "react";
const ImgAdd = (props) => {
const [imgView, setImgView] = useState("");
const imgRef = useRef();
const saveImgView = () => {
const file = imgRef.current.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImgView(reader.result);
};
console.log(imgView);
};
return (
<div className="img-add-box">
{imgView && (
<div className="img-view">
<p>미리보기</p>
<div className="img-view__wrapper">
<div className="img-view__img">
<img src={imgView} alt="" />
</div>
<button
className="btn img-view__btn"
onClick={() => {
setImgView();
}}
>
삭제하기
</button>
</div>
</div>
)}
{!imgView && (
<div className="img-add-box__wrapper">
<div className="img-add-box__plus">
<span className="material-symbols-outlined">add_circle</span>
</div>
<p>이미지를 추가해주세요</p>
<label htmlFor={props.id} className="img-add-box__label btn">
업로드
</label>
<input
id={props.id}
type="file"
accept="image/*"
style={{ display: "none" }}
onChange={saveImgView}
ref={imgRef}
/>
</div>
)}
</div>
);
};
export default ImgAdd;
실제 화면은 아래와 같다.
이미지를 각각 컴포넌트 내에서 관리하였더니,
부모컴포넌트인 Admin에서 form 을 제출하여
백엔드로 데이터를 저장하고 싶었으나 안되었다.
그래서 부모컴포넌트에서
state여러개를 한개의 state에 object로 묶어 관리하려고 했다.
각각의 이미지를 업로드하고 업로드 된 컴포넌트에만
이미지 미리보기를 보고 싶었다.
하고자 하는 것 :
부모컴포넌트에서 이미지 state를 관리할 때,
따로 이미지 state를 관리할 방법을 찾고 싶었다.
확실하게 배운것 :
키 밸류 페어로 스테이트를 관리하려면
object로 state초깃값을 설정, 관리해야 한다.
그러면 순서에 상관없이
유저가 업로드 한대로
마음대로 이미지를 미리보기 할 수 있다.
Admin 컴포넌트 중 일부
import { useEffect, useReducer, useState } from "react";
import ImgAdd from "../components/UI/ImgAdd";
import Tbody from "../components/Tbody";
import Trow from "../components/Trow";
import TdataTitle from "../components/TdataTitle";
import Tdata from "../components/Tdata";
import Select from "../components/Select";
const Admin = () => {
const [imgView, setImgView] = useState({});
useEffect(() => {
console.log("titleIsValid", titleIsValid);
console.log("selectIsValid", selectIsValid);
setFormIsValid(titleIsValid && selectIsValid);
console.log("formIsValid", formIsValid);
}, [titleIsValid, selectIsValid, formIsValid]);
//ImgAdd 컴포넌트에 props로 전달해주기
//절대 setState그대로 전달하지 않기
//props는 읽기전용이므로 수정불가
//imgUrl과 imgId는 자식 컴포넌트에서 imgHandler에서 전달해준 파라미터
const imgHandler = (imgUrl, imgId) => {
console.log("imgHandler");
setImgView({ ...imgView, [imgId]: `${imgUrl}` });
console.log(imgView);
};
//form을 submit했을 때 실행됨 현재는 입력한 state를 콘솔에 보여줌
const submitHandler = (e) => {
e.preventDefault();
console.log(titleState, selectState, imgView);
};
return (
<section className="content">
<div className="content__wrapper">
<form action="submit" onSubmit={submitHandler}>
<table className="table">
<colgroup>
<col className="table_col-1" width="20%" />
<col className="table_col-2" width="80%" />
</colgroup>
<thead className="table__head">
<tr>
<th colSpan={4}>제품 등록하기</th>
</tr>
</thead>
<Tbody>
<Trow>
<TdataTitle>
<label htmlFor="productTitle">상품명</label>
</TdataTitle>
<TdataTitle>
<div className="table__cate">
<input
className="table__input"
id="productTitle"
type="text"
onChange={titleValChangeHandler}
onBlur={titleBlurHandler}
value={titleState.value}
/>
<Select
options={optionArray}
selectHandler={selectHandler}
selectBlurHandler={selectBlurHandler}
selectState={selectState}
/>
</div>
</TdataTitle>
</Trow>
<Trow>
<TdataTitle>기본이미지</TdataTitle>
<Tdata>
<div className="table__data--add">
<ImgAdd
id="img1"
imgHandler={imgHandler}
imgView={imgView}
/>
</div>
</Tdata>
</Trow>
<Trow>
<TdataTitle>
<label htmlFor="imgUrl">추가이미지</label>
</TdataTitle>
<Tdata>
<div className="table__data--add">
<ImgAdd
id="img2"
imgHandler={imgHandler}
imgView={imgView}
/>
<ImgAdd
id="img3"
imgHandler={imgHandler}
imgView={imgView}
/>
<ImgAdd
id="img4"
imgHandler={imgHandler}
imgView={imgView}
/>
{/* */}
<ImgAdd
id="img5"
imgHandler={imgHandler}
imgView={imgView}
/>
</div>
</Tdata>
</Trow>
<Trow>
<TdataTitle>
<label htmlFor="imgUrl">제품 설명</label>
</TdataTitle>
<Tdata>
<div className="table__textarea">
<textarea
name=""
id=""
rows="10"
className="table__textarea-item"
></textarea>
</div>
</Tdata>
</Trow>
</Tbody>
</table>
<button
className="btn form__button"
type="submit"
onSubmit={submitHandler}
disabled={!formIsValid}
>
등록하기
</button>
</form>
</div>
</section>
);
};
export default Admin;
ImgAdd 컴포넌트
const ImgAdd = (props) => {
const imgRef = useRef();
const imgId = props.id;
//저장된 이미지 미리보기
const saveImgView = (e) => {
const file = imgRef.current.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
props.imgHandler(reader.result, e.target.id);
};
};
return (
<div className="img-add-box">
{/*해당 아이디의 컴포넌트에 이미지가 올라가면 보이는 것들*/}
{props.imgView[imgId] && (
<div className="img-view">
<p>미리보기</p>
<div className="img-view__wrapper">
<div className="img-view__img">
<img src={props.imgView[imgId]} alt="" />
</div>
<button
className="btn img-view__btn"
onClick={() => {
props.imgHandler("", imgId);
}}
>
삭제하기
</button>
</div>
</div>
)}
{/*해당 아이디의 컴포넌트에 이미지가 없으면 보이는 것들 */}
{!props.imgView[imgId] && (
<div className="img-add-box__wrapper">
<div className="img-add-box__plus">
<span className="material-symbols-outlined">add_circle</span>
</div>
<p>이미지를 추가해주세요</p>
<label htmlFor={imgId} className="img-add-box__label btn">
업로드
</label>
<input
id={imgId}
type="file"
accept="image/*"
style={{ display: "none" }}
onChange={saveImgView}
ref={imgRef}
/>
</div>
)}
</div>
);
};
export default ImgAdd;
콘솔 화면에 찍힌 imgView 스테이트
img4등은 컴포넌트의 id이다.
img4먼저 이미지를 업로드 하면
img4의 컴포넌트에만 업로드된 이미지가 보이게 된다.
'공부기록 > React' 카테고리의 다른 글
선언형과 명령형 (0) | 2023.11.15 |
---|---|
컴포넌트의 리렌더링 조건 (0) | 2023.11.03 |
[React] state를 변경 할 때 spread 문법을 이용하는 이유 (공부내용 정리) (원시값 개념) (0) | 2022.07.17 |
[react] useReducer Hook 이해하기 (0) | 2022.07.16 |