Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- TDD
- MVC요청플로우
- MVC
- 사면초가
- 공통규약
- 커뮤니티서버
- index
- 로그백
- 공통기술
- 냄새라도
- AOP
- 단위테스트
- Ajax
- 설정세팅
- JDBC
- SEQUENCE
- 디스크i/o
- 이벤트핸들러등록
- 이벤트핸들러this
- model
- 클린아키텍처
- JPA
- 전전긍긍
- string
- DDD
- 문제선택근거
- SpringLegacy
- springsecurity
- Transaction
- OOP
Archives
- Today
- Total
개발이군고구마
[React] 컴포넌트를 리랜더링 하기 위해 state 조작하기 (위시리스트) 본문
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>
</>
);
데이터가 바뀔 때마다 해당하는 이미지가 보여짐

*전체 코드
'FRONT' 카테고리의 다른 글
| [JS] 이벤트 핸들러 방식과 this(DOM의 attribute/property) (0) | 2024.10.26 |
|---|---|
| [JS] 브라우저 렌더링 과정 (캐싱/SSR) (0) | 2024.09.13 |
| [JS] aysnc와 await의 작동 순서 (Response.json()으로부터 촉발된) (0) | 2024.07.21 |
| [JS] 자바스크립트 동작 원리 2 (비동기와 이벤트루프) (0) | 2024.06.30 |
| [JS] 자바스크립트 동작 원리 1 (스레드, 동기, 비동기) (0) | 2024.06.22 |