-
setState가 아닌 방식으로 값을 변경했을 때에도 렌더링되도록 하기Today I Learned 2022. 9. 13. 23:19
퀘스트 과제로 주어진 마카오 페이를 작성하고 있다. 오늘 구현했던 부분 중 어떻게 구현해야 할지 가장 고민이었던 부분은 다음과 같다.
- 친구 목록에서 특정 친구를 클릭하면 선택되고 테두리에 선택된 표시가 뜬다. (N빵 정산에서 사용)
바로 이런 식으로.
프로그램에서는 친구 목록을 friends라는 배열로 선언하고, 주 컴포넌트 내에서 useState로 관리되도록 하고 있다. 배열을 구성하는 하나의 요소는 { id, name, selected } 값을 갖는 객체가 되도록 했는데, selected 변수가 boolean 값을 가져서 해당 객체가 클릭되었는지 클릭되지 않았는지를 나타내는 기준값으로 사용하고자 했다.
배열의 각 요소들은 친구 목록임을 나타내기 위해 <ul> 태그 내부에 <li> 태그들을 배열 요소의 개수만큼 선언한 뒤, 각 <li> 태그에 name을 값으로 주고 id를 <li>의 key로 부여했다. 이때 썸네일을 클릭했을 때 발생하는 클릭 이벤트를 활용하기 위해 name을 <button> 태그로 둘러싸고 클릭 이벤트 함수를 정의했다.
바로 이 클릭 이벤트 함수 내에서 해당 친구 요소의 selected 변수를 반대로 뒤집고, React가 상태가 변경된 배열을 이용해 화면을 리렌더링하도록 해서 해당 요구사항을 만족시키고자 했다. 이렇게 할 경우, N빵 정산 컴포넌트에서 배열의 요소들 중 selected가 true인 것들만을 활용하도록 할 수 있기 때문에 N빵 정산 컴포넌트를 구현하는 데 드는 시간을 절약할 수 있을 것이라는 기대감도 있었다.
프로그램에서는 selected 변수를 뒤집어야 할 친구 요소를 찾기 위해 find 함수를 이용했고, 찾아낸 친구 요소의 selected 값을 직접 바꾸도록 했다.
const changeSelectedFriendStatus = (selectedFriendId) => { const selectedFriend = friends.find((friend) => ( friend.id === selectedFriendId )); selectedFriend.selected = !selectedFriend.selected; }
문제는 이렇게 값을 바꾸는 것으로는 React가 렌더링을 다시 해야 함을 인지하지 못하기 때문에 값이 바뀌더라도 화면에는 그 바뀐 상태를 적용시키지 못한다는 점이었다.
그렇다면 렌더링을 명시적으로라도 시켜줄 수 있도록 해야겠다는 생각이 들어 인터넷에 'React 부분 렌더링'을 키워드로 정보를 찾아보았다. 찾은 정보들의 내용을 나름대로 적용시켜 보았다. selected를 바꾼 뒤, 컴포넌트 안에서 root를 다시 정의해서 render에 this (현재 컴포넌트?) 를 전달해본다던가, this.forceUpdate()를 호출한다던가를 시도해 봤지만 되지 않았다.
다시 확인해보니 해당 방법들은 컴포넌트가 class 형식으로 정의되어 있을 때 사용이 가능한 방법이었다. 프로그램에서는 컴포넌트가 함수 형식으로 정의되어 있었기 때문에 사용할 수 없었다.
그래서 이번에는 함수형 컴포넌트에서도 명시적인 리렌더링을 할 수 있는지를 찾아보았다. 'react functional component rerender'라는 키워드로 검색했을 때 나온 검색 결과 중 첫 번째로 나온 Stack Overflow 글의 제목이 내 상황과 맞는 것 같아 들어가 보았다.
글에서 제시된 방법들은 단순히 렌더링만을 위한 useState를 정의하고, 렌더링을 원하는 시점에 해당 useState의 setState를 호출하는 방식이었다. 일종의 뭐랄까... setState의 호출은 리렌더링을 수반하기에 그것을 활용하는 것 같았다. 결국은 setState를 쓰게 만드는 느낌? 좀 더 명시적으로 리렌더링을 호출하는 방식을 찾아보고 싶었지만 일단 아쉬운 대로 방법들 중 하나를 소스코드에 적용해 보았다.
const [, updateState] = useState(); const forceUpdate = useCallBack(() => updateState({}), []); // ... const changeSelectedFriendStatus = (selectedFriendId) => { const selectedFriend = friends.find((friend) => ( friend.id === selectedFriendId )); selectedFriend.selected = !selectedFriend.selected; forceUpdate(); }
다행이 의도한 대로 화면을 리렌더링해서 변환된 값을 표출해주는 것을 확인할 수 있었다.
과제를 수행하면서 당장 배우지 못한 모르는 부분이 나오더라도 이 문제점을 해결하기 위해 여기에서 쓰이는 어떤 부분을 내가 좀 더 알아야 할까?를 추론하면서 내가 찾아야 하는 타켓의 범위를 좁혀나가는 과정이, 모르겠는 것을 찾아가는 과정에서 굉장히 중요한 부분인 것 같다.
즉 '내가 애매하게 잘 모르는 것들을 잘 검색해서 적어도 검색하는 그 순간에는 이해하고 쓸 수 있고, 더 나아간다면 내가 아는 것으로 확실히 바꿔나가는 과정을, 적은 시간을 쓰고도 그렇게 되게끔 하게 하는 게' 주말을 바쳐서 그 주에 다뤄야 할 개념들을 최대한 많이 접하고, 진짜 잘 모르겠더라도 어떻게든 꾹 참고 개념을 계속해서 봐야 하는 이유가 아닌가 싶다.
'Today I Learned' 카테고리의 다른 글
git rebase 명령어 실험 (0) 2022.09.17 테스트 코드로 스토리텔링을 (0) 2022.09.14 '큰 일을 하기 위해 지금 할 수 있는 것부터 한다' (0) 2022.09.12 친구가 내준 문제를 푸는 데 써본 TDD 프로세스 (0) 2022.09.12 개발 그 이상의 영역? (0) 2022.09.10 - 친구 목록에서 특정 친구를 클릭하면 선택되고 테두리에 선택된 표시가 뜬다. (N빵 정산에서 사용)