개발이군고구마

[React] 컴포넌트를 리랜더링 하기 위해 state 조작하기 (위시리스트) 본문

FRONT

[React] 컴포넌트를 리랜더링 하기 위해 state 조작하기 (위시리스트)

김구황 2024. 6. 16. 00:08
728x90

 Manipulating useState to trigger Re-rendering 

 

1. 주어진 상황  

  • 전체 호스트 조회 시 등록된 호스트 정보들을 전체로 조회함 
  • 로그인된 사용자에 따라 위시리스트에 등록된 호스트 별로 위시리스트 체크 / 미체크 
  • 위시리스트 테이블 
    • 위시리스트는 - 호스트 번호를 가지고 있음 
  • 위시리스트가 클릭될 때마다 -> useContext를 사용해서 Gnb 에 반영하고 있음 
  • useContext의 reducer 함수가 실행되고 나서 각각 위시리스트 테이블 추가/삭제 로직이 들어감

 

 

2. 문제상황 

  • 위시리스트에 데이터의 변형은 useContext 에 반영 후, fetch 를 통해 반영하였으나
  • JSX 부분에 그 변화를 감지하는 부분이 없기 때문에 위시리스트 체크/ 미체크 표시가 되지 않고 있었음

 

1) state를 하나로 관리하는 경우 

  • 호스트 loop - 위시리스트 정보 추출 이기 때문에 각 호스트별 위시리스트를 구분할 수 없음 
  • 모든 host 별로 state가 존재해야 구분이 가능하지만, state는 DB 에서 조회할 수 있는 값이 아님 
const [chosenWish, setChosenWish] = useState(false);

...

<button
  type="button"
  aria-label="위시리스트에 저장"
  onClick={
    chosenWish
      ? () => wishItemRemoveHandler(host.hnum)
      : () => wishItemAddHandler(host.hnum)
  }
>
  {chosenWish ? (
    <img
      src={fullWishList}
      alt="wishlist"
      style={{ width: "10px", height: "10px" }}
    />
  ) : (
    <img
      src={emptyWishList}
      alt="wishlist"
      style={{ width: "10px", height: "10px" }}
    />
  )}
</button>

 

그렇다면 위시리스트의 정보도 부모 페이지에서 들고와야 함 

 

2)  검증 함수를 따로 만드는 방법

  • 함수를 실행할 이벤트가 없음 
  const wishItemCheckHandler = (hnum) => {
    return wishList.includes(hnum);
  };

{hostsList.map((host) => (
  <li key={host.hnum}>
    <img
      src={host.hostMainImg.fileUri}
      alt={host.hostMainImg.filename}
      style={{ width: "200px", height: "150px" }}
    />
    <Link to={host.hnum.toString()}>{host.shortintro}</Link>
    <button
      type="button"
      aria-label="위시리스트에 저장"
      onClick={() => wishItemContextCheckHandler(host.hnum)}
    >
      {wishItemCheckHandler(host.hnum) ? (
        <img
          src={fullWishList}
          alt="wishlist"
          style={{ width: "10px", height: "10px" }}
        />
      ) : (
        <img
          src={emptyWishList}
          alt="wishlist"
          style={{ width: "10px", height: "10px" }}
        />
      )}
    </button>

 

 

3. 개념

[React의 Render와 Commit]

https://react.dev/learn/render-and-commit

  • Initial render  
createRoot(document.getElementById('root'))

 

  • Re-renders when state updates 
    • state가 update 됬다는 것을 감지하게 되면
    • state는 queue에 변화를 쌓아놈 ( = react를 trigger)  > 해당 이벤트가 종료가 되면 
    • trigger 된 react가 해당 component 를 불러서 변경이 요청되었다고 알림 (=Rendering)
    • component가 새로운 값(JSX snapshot)을 반환함
    • 변화된 부분에 한해서 React가 DOM nodes 들을 변경함

 

  • rendering 원리 코드에 적용하기 
    • wishList의 데이터가 변경이 된 후, 그에 따라 변경이 필요한 JSX component가 있다면,
    • 변경이 필요한 부분이 감지 될 수 있도록 해야함 

 

 

 

4. 해결 방안

  • React 를 trigger 하기 위해서 
  • 변화가 필요한 부분을 리렌더링 하게 하는 작업이 필요했음 

 

 

1) 기존에 있었던 정보를 변경시켜야 했기 때문에, 부모 페이지 HostSearchPage.js에서

  • wishList와 이를 변경시킬 수 있는 set function을 함께 넘김 
      <HostList hostsList={hostsList} wishList={wishList} setWishList = {setWishList} />
  • 중복 loader 사용함 
  export async function loader() {
  let headers = new Headers({
    "Content-Type": "application/json",
  });

  const accessToken = localStorage.getItem("ACCESS_TOKEN");
  if (accessToken && accessToken !== null) {
    headers.append("Authorization", "Bearer " + accessToken);
  }

  const [responseHost, responseWish] = await Promise.all([
    fetch("http://localhost:8080/api/host/list", {
      method: "GET",
      headers: headers,
    }),
    fetch("http://localhost:8080/api/wishList/list", {
      method: "GET",
      headers: headers,
    }),
  ]);

  if (!responseHost.ok || !responseWish.ok) {
    throw json(
      { message: "Could not fetch events." },
      {
        status: 500,
      }
    );
  } else {
    let hosts = await responseHost.json();
    let wishs = await responseWish.json();

    return { hosts, wishs };
  }
  
  ....
  
  return (
    <>
      <HostOption setHostsList={setHostsList} />
      {/* 검색에서 찾음 */}
      <HostList hostsList={hostsList} wishList={wishList} setWishList = {setWishList} />
      {/* 검색에서 찾은 값을 리스트로 보냄 */}
      <HostMap hosts={hostsList} />
    </>
  );

 

 

 

2) HostList.js wishList의 값이 변경될 때마다, set function을 통해서 state 값에 변경을 줌으로써 

해당 state를 사용하는 컴포넌트들을 re rendering 할수 있도록 함

newWishList.then((result)=> setWishList(result));
const wishItemAddHandler = async (id) => {
    wishCtx.addHost(id);

    let headers = new Headers();

    const accessToken = localStorage.getItem("ACCESS_TOKEN");
    if (accessToken && accessToken !== null) {
      headers.append("Authorization", "Bearer " + accessToken);
    }

    // action.hnum fetch INSERT
    const response = await fetch("http://localhost:8080/api/wishList/save", {
      method: "POST",
      headers: headers,
      body: id,
    });

    if (response.state === 422) {
      return response;
    }
    if (!response.ok) {
      throw json({ message: "Could not save board." }, { status: 500 });
    }
    const newWishList = response.json();
    newWishList.then((result)=> setWishList(result));
    ** 변화를 감지함**

  };

 

 

 

3) wishList의 변화를 감지

  • 해당 state 가 있는 부분이 다시 re-rendering 되고, 
  • 반환된 값을 바탕으로 react는 변화된 부분에 한해서 DOM 을 수정 
wishList.findIndex(wish => wish.hostNum === hnum) !== -1?
  return (
    <>
      <ul>
        {hostsList.map((host) => (
          <li key={host.hnum}>
            <img
              src={host.hostMainImg.fileUri}
              alt={host.hostMainImg.filename}
              style={{ width: "200px", height: "150px" }}
            />
            <Link to={host.hnum.toString()}>{host.shortintro}</Link>
            <button
              type="button"
              aria-label="위시리스트에 저장"
              onClick={() => wishItemContextCheckHandler(host.hnum)}
            >
              {wishList.findIndex(wish => wish.hostNum === hnum) !== -1? (
                <img
                  src={fullWishList}
                  alt="wishlist"
                  style={{ width: "10px", height: "10px" }}
                />
              ) : (
                <img
                  src={emptyWishList}
                  alt="wishlist"
                  style={{ width: "10px", height: "10px" }}
                />
              )}
            </button>
          </li>
        ))}
      </ul>
    </>
  );

데이터가 바뀔 때마다 해당하는 이미지가 보여짐 

 

 

*전체 코드 

https://github.com/JEEEEEEHO/wwoof-frontend.git