본문 바로가기

공부기록/React

[React] 리액트로 state여러개 object로 관리하기

상황: 어드민페이지를 만드는 중이었다.

 

이미지 추가하여 미리보여주는 컴포넌트는 한개이며, 아래와 같다.

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의 컴포넌트에만 업로드된 이미지가 보이게 된다.