value
로 정의하기defaultValue
로 정의하기defaultValue
로 설정시 selected가 안될때, key
속성과 같이 사용하기 (예: key={value} defaultValue={value}
)셀렉트 박스를 기준으로. 일반적인 javascript와 html에서는 selected
속성을 사용해 선택된 기본값을 설정한다. 하지만 리액트에선 selected
속성 대신 value
를 사용해 기본값을 정의한다.
<select value="2">
<otpion value="1">1</option>
<otpion value="2">2</option>
</select>
일반적으로 value로 기본값을 사용하면 값이 읽기전용으로 취급되어 값이 변경되지 않는다. (사진상 첫번째 컴포넌트) 이때 defaultValue 속성을 사용하면, 비제어 컴포넌트로 활용하면서 변경되는 값으로 사용할 수 있다.
React 렌더링 생명주기에서 폼 엘리먼트의 value 어트리뷰트는 DOM의 value를 대체합니다. 비제어 컴포넌트를 사용하면 React 초깃값을 지정하지만, 그 이후의 업데이트는 제어하지 않는 것이 좋습니다. 이러한 경우에 value 어트리뷰트 대신 defaultValue를 지정할 수 있습니다.
하지만 defaultValue에 사용될 value값을 부모 컴포넌트에서 내려줄 때, 외부에서 받아온 데이터 값 (api 요청으로 받아온 값)으로 내려주면 값이 선택되지 않는 문제가 있었다. (사진상 두번째 컴포넌트)
When the source prop changes, the loading state should always be overridden. Conversely, the state is overridden only when the prop changes and is otherwise managed by the component.
처음에는 컴포넌트의 마운트/언마운트와 관련된 줄 알고 useEffect()
와 setState()
에서 내려받은 값을 다시 관리했었는데, 이는 안티패턴 이었다. props로 전달받은 값을 state에 복사하면, 이후의 모든 업데이트 값이 무시된다. 관련된 설명은 Dan Abramov의 블로그에 잘 설명되어 있다.
react의 공식문서에도 나와있듯이 이런 상황에서 권장되는 방법은 제어 컴포넌트 방식인 value
를 사용하는 것과 완전한 비제어 컴포넌트 방식을 사용하는 것인데, 제어 컴포넌트를 사용하면 위와 같이 값을 변경할 수 없게 되므로 나는 후자의 방식을 선택했다.
<select key={value} defaultValue={value}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
기본값도 제대로 잘 적용되고, 값도 잘 변경되는 모습..!
이 방식은 키가 변경될 때 마다 모든 요소들이 갱신되고 재생성된다. key방식에 대한 자세한 설명은 여기에 잘 나와있다.
DOM 노드의 자식들을 재귀적으로 처리할 때, React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성합니다.
예를 들어, 자식의 끝에 엘리먼트를 추가하면, 두 트리 사이의 변경은 잘 작동할 것입니다.
자식들이 key를 가지고 있다면, React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인합니다. 인덱스를 key로 사용 중 배열이 재배열되면 컴포넌트의 state와 관련된 문제가 발생할 수 있습니다. 컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용됩니다. 인덱스를 key로 사용하면, 항목의 순서가 바뀌었을 때 key 또한 바뀔 것입니다.
21.07.29 추가
외부 API를 통해 받아오는 값의 경우 key를 value와 같은 값으로 설정해두면 값이 제대로 선택되지 않는 오류가 있다. 이 경우에는 key가 유니크한 값이 아니어서 이러한 오류가 생길 수 있다.
Math.random()
과 같은 값으로 key를 랜덤하게 지정해줄 수 있지만, 공식문서에 의하면 다음과 같은 이유로 비추천한다.
key는 반드시 변하지 않고, 예상 가능하며, 유일해야 합니다. 변하는 key(Math.random()으로 생성된 값 등)를 사용하면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하여 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있습니다.
한마디로 변하는 값으로 key를 지정하면 성능 부분에서 문제가 생길 수 있음을 얘기한다. 그래서 찾은 해결책이 uuid를 사용하는 방법인데, key를 uuid의 고유한 값으로 설정해두면 외부에서 받아온 값으로 selected를 지정해도 확실히 잘 선택된다.
npm i react-uuid
import uuid from "react-uuid"
// ...
return (
<select key={uuid()} value={value}>
</select>
)
이 문제로 며칠간 많이 고민했는데, 생각외로 간단한 문제였다. 오늘도 역시 공식문서는 위대하다는 깨달음을 얻게 되었다... 또, 그냥 쓰라고 해서 썼던 key요소의 중요성을 다시한번 깨닫게 되는 순간이었다.
의역과 축약이 많이 들어가 있는데, 자세한 원문 내용은 여기에서 확인하는 걸 권장한다.