useState에 대한 정리가 아닌 어떻게 사용했었는지에 대해 정리.
프로젝트를 진행하면서 useState를 통해 한개의 데이터만을 관리하기도 했지만 여러개의 데이터가 들어가있는 객체 또는 배열을 관리해야 했다.
예를들어 페이징 기능을 수행하기 위해 pageNum을 관리한다고 하면 간단하게 setPageNum(2) 이런 식으로 처리하고 관리하기 쉬웠으나 객체 또는 배열을 관리하는건 다르게 처리해야 했기에 정리.
//pageNum. 단일 데이터 초기값을 1로 설정
const [pageNum, setPageNum] = useState(1);
//게시글 input 값 관리
//title, content를 담고 있는 객체 구조. 초기값은 둘다 '' 로 설정
const [values, setValues] = useState({
title: '',
content: '',
});
//게시글 리스트 값 관리
//페이지 접근 시 리스트를 담아 처리.
//기본값으로 [] 빈 배열로 설정.
const [boardData, setBoardData] = usetState([]);
//단일 데이터 set
setPageNum(2);
//객체 데이터 set
setValues({
title: 'setTitle',
content: 'setContent',
});
//객체 데이터 중 하나의 데이터만 set
setValues({
...values,
title: 'setTitle2',
});
//위 하나의 객체 데이터를 수정하는 set 활용
//onChange가 발생할 때 state를 set
setValues({
...values,
[e.target.name]: e.target.value,
});
//리스트 데이터 set
setBoardData(response.data.content);
//배열 데이터 set
const boardArray = [];
boardArray.push({data: data1, ...});
boardArray.push({data: data2, ...});
//...
setBoardData(boardArray);
//boardData에 객체를 추가하는 경우
const boardArray = [...boardData];
boardArray.push({data: data10, ...});
//...
setBoardData(boardArray);
프로젝트를 진행하면서 useState는 이렇게 사용했다.
가장 간단한 단일 데이터는 React를 학습하면서 배운 useState 사용법 그대로 사용.
객체까지도 학습한 내용만으로 해결이 가능했다.
객체의 경우 단순하게 모든 데이터를 직접 작성해 set을 해주는 방법이 있고, 그 객체 안에서 하나의 값만 수정하고자 한다면 다르게 처리해야 했다.
위 코드처럼 title, content 중 title 하나만 값을 변경하고자 한다면 content의 값은 유지되어야 한다.
이때 사용할 수 있는 방법이 전개 연산자(Spread Operator)를 사용하는 것이다.
전개연산자는 ES6 문법으로 배열 또는 객체를 넘기는 용도로 사용된다.
...values의 의미는 기존에 values에 담겨있는 값을 그대로 담아준다고 볼 수 있다.
그럼 위 코드에서 ...values를 통해 title: 'setTitle1', content: 'setContent1'이 담기게 될 것이고 그 이후 title을 setTitle2로 바꾸게 되면서 set 되는 데이터는 title: 'setTitle2', content: 'setContent2' 가 된다.
이걸 input에서 onChange 핸들링으로 처리하도록 하는 것으로 입력이 끝난 후 submit이 발생했을 때 values값을 확인하는 것으로 input 들의 값을 알아낼 수 있다.
...values로 다른 input 데이터는 다시 담아 유지할 수 있도록 하고 e.target.name 과 e.target.value로 input에 작성해둔 name값, 작성한 value 값을 가져와 set 해주게 된다.
그럼 입력창의 value를 좀 더 수월하게 관리할 수 있게 된다.
그리고 마지막 배열.
axios 요청에 대한 응답으로 리스트 데이터를 받는 경우 간단하게 res.data를 set 해주는 것으로 배열형태로 담을 수 있다.
문제는 배열을 직접 제어해야 하는 경우다.
배열을 직접 제어하는 것이 가장 처음 렌더링 될때만 수행하게 되는 경우도 있지만 매번 직접 제어해야 하는 경우도 있다.
예를들어 이미지 파일 업로드에서 업로드할 파일과 중간에 사용자가 삭제 버튼을 눌러 사진을 제거했을때의 경우가 있다.
사용자가 파일 5개를 업로드 하고자 선택을 하면 가장 먼저 배열에 파일들을 담아 setImageData 같이 처리하게 될 것이다.
근데 3번째 파일을 지우려고 삭제 버튼을 눌렀다면?
set 된 데이터들 중 3번째 파일 데이터를 삭제하고 나머지 데이터만 남겨야 한다.
하지만 useState는 readOnly로 수행된다.
그래서 imageData 라는 state에서 특정 데이터를 삭제하고 나머지를 set 해주기 위해서는 배열을 사용해야 했다.
이때 처리한 방법이 가장 마지막 코드와 같은 방법이다.
삭제 버튼의 onClick이 발생했을 때 handleOnClick에서는 비어있는 새로운 배열에 전개 연산자로 state 값을 그대로 담아준다.
그리고 3번째 파일이니 2번 인덱스에 대한 값을 찾는다.
그리고 그 파일을 배열에서 제거한 뒤 setState를 통해 set을 해주게 되면 사용자의 요청대로 특정 위치의 데이터만 삭제할 수 있게 된다.
마지막으로 useState를 사용하며 주의해야 할 점.
React 학습에서는 보통 초기값을 설정하지 않고 처리하는 경우가 많았다.
그리고 초기값을 잘못 설정하는 경우 재 렌더링 될때마다 의도하지 않은 값이 설정될 수 있기 때문에 잘 고민해서 초기값 설정을 해야 한다고도 들었다.
프로젝트를 진행하면서 문제가 발생한 부분들은 대부분 초기값에 대한 문제가 많았다.
대부분의 컴포넌트에서 서버에 데이터를 요청한 뒤 setData 에 담아주도록 하고 있는데 이 axios 요청은 useEffect에서 보내도록 해두었다.
거의 첫 렌더링에서만 수행하면 되는 요청이기 때문이라는 생각이었기 때문에 useEffect를 사용해 호출하도록 했는데 문제는 useEffect는 렌더링 후에 수행된다는 점이었다.
해당 컴포넌트가 하위 컴포넌트를 갖지 않고 처리한다면, 또는 해당 값이 null이라도 렌더링에서 오류가 발생하지 않는다면 초기값은 이상하게 설정하지만 않으면 문제가 되지 않았다.
하지만 리스트와 같이 하위 컴포넌트를 두고 그 하위 컴포넌트를 map을 통해 반복 하도록 했다면 얘기가 다르다.
반복문을 통해 처리하라고 했으면 '얘는 배열 타입이야' 라고 알려줘야 한다.
근데 state의 초기값을 아무것도 설정하지 않고 얘로 반복문 돌려서 처리해줘 라고 하면 오류가 발생하는 것.
'단일 객체를 반복문을 왜 돌림?' 이런 느낌이다..
그래서 state의 초기값을 [] 로 빈 배열로 초기화를 해둬야 한다.
혹은 배열이 아니더라도 하위 컴포넌트에서 해당 state 값으로 무언가를 처리하는데 오류가 발생할 여지가 있다면 초기값을 설정해 오류가 발생하지 않도록 해야 한다.
배열 state 코드가 게시글 리스트 코드를 예로 들었으니 게시글 리스트를 예로 들어본다.
리스트 페이지 접근 -> 렌더링 -> useEffect 수행 -> axios 요청, 응답 후 setData -> state가 변경되었으니 재렌더링.
이 순서로 발생하기 때문에 1차 렌더링 시에 오류가 발생해 useEffect가 제대로 수행되지 않거나 수행되더라도 재 렌더링이 수행되지 않게 된다.
주의!!
이 문제에 대한 참고 블로그
[트러블슈팅][React] useEffect는 렌더링 '이후'에 실행된다
useEffect의 동작 및 컴포넌트 렌더링 과정에 대한 이해도가 부족해 발생한 문제였습니다. 의존성 배열을 사용하여 해결했지만, useLayoutEffect를 사용해볼 여지가 남아 있습니다.
velog.io
'Front > React' 카테고리의 다른 글
React에서 state 배열 관리 및 반복문에서의 state (0) | 2024.04.13 |
---|---|
React 페이지 이동 (1) | 2024.04.12 |
React useParams(), useSearchParams() (1) | 2024.04.12 |
React에서 .env 사용 (0) | 2024.04.11 |
React Axios 모듈화 (0) | 2024.04.11 |