아래 코드 중 어떻게 조건부 렌더링을 사용하고 있었나요?
{props.title ? (
<strong className="text-gray-800 font-semibold text-xl">
{props.title}
</strong>
) : null}
{props.title ? (
<strong className="text-gray-800 font-semibold text-xl">
{props.title}
</strong>
) : undefined}
{props.title && (
<strong className="text-gray-800 font-semibold text-xl">
{props.title}
</strong>
)}
간만에 서비스 개발을 하면서 제각각인 코드가 눈에 보였다. @wise님께서는 아래와 같은 리뷰도 남겨주셨는데,
@wise: 삼항연산자를 사용하는 과정에 return null 인 경우 &&로 변경하는게 가독성이 좋을 것 같습니다.
같은 코드라면 가독성이 좋은 &&를 사용하는게 맞아 보였다. 과연 같을까?
TL;DR
JSX 조건부 렌더에 &&구분자를 사용하면 알 수 없는 값(NaN 또는 0)이 렌더될 수 있습니다, 또는 렌더링이 중단될 수 있습니다.
const Example = () => {
return (
<>
{0 && <Something />}
{/* React: 0이 렌더링됨 */}
{/* React Native: crashes 💥 */}
{NaN && <Something />}
{/* React: NaN이 렌더링됨 */}
{/* React Native: crashes 💥 */}
{'' && <Something />}
{/* React: 아무것도 렌더링 되지 않음 */}
{/* React Native, with React < 18: crashes 💥 */}
</>
)
}
해결 방법으로는 두 가지 방법을 제시합니다.
{someValue ? <Something /> : null}
{!!someValue && }
잘못된 방법의 예시
const Component = ({ count, title }) => {
return <div>{count && title}</div>
}
const Component = ({ count }) => {
return <div>{count && <span>There are {count} results</span>}</div>
}
const Component = ({ elements }) => {
return <div>{elements.length && <List elements={elements} />}</div>
}
const Component = ({ nestedCollection }) => {
return (
<div>
{nestedCollection.elements.length && <List elements={nestedCollection.elements} />}
</div>
)
}
const Component = ({ elements }) => {
return <div>{elements[0] && <List elements={elements} />}</div>
}
const Component = ({ numberA, numberB }) => {
return <div>{(numberA || numberB) && <Results>{numberA + numberB}</Results>}</div>
}
// 조건이 부울 값인 경우 이 규칙은 논리식을 eslint에 보고합니다.
// 조건의 유형을 추론할 수 없기 때문입니다.
const Component = ({ someBool }) => {
return <div>{someBool && <Results>{numberA + numberB}</Results>}</div>
}
올바른 방법의 예시
const Component = ({ elements }) => {
return <div>{elements}</div>
}
// OR 조건은 방법으로 간주되므로 유효한 것으로 간주됩니다.
// 첫 번째 값이 거짓인 경우 일부 대체를 렌더링하고 조건부로 렌더링하지 않습니다.
const Component = ({ customTitle }) => {
return <div>{customTitle || defaultTitle}</div>
}
const Component = ({ elements }) => {
return <div>There are {elements.length} elements</div>
}
const Component = ({ elements, count }) => {
return <div>{!count && 'No results found'}</div>
}
const Component = ({ elements }) => {
return <div>{!!elements.length && <List elements={elements} />}</div>
}
const Component = ({ elements }) => {
return <div>{Boolean(elements.length) && <List elements={elements} />}</div>
}
const Component = ({ elements }) => {
return <div>{elements.length > 0 && <List elements={elements} />}</div>
}
const Component = ({ elements }) => {
return <div>{elements.length ? <List elements={elements} /> : null}</div>
}
const Component = ({ elements }) => {
return <div>{elements.length ? <List elements={elements} /> : <EmptyList />}</div>
}
규칙 설정하기
ValidStrategies 옵션으로 "coerce"와 "tenary"를 또는 둘 다 ["tenary", 'coerce"]와 같이 줄 수 있습니다.
- coerce는 조건부 jsx expression을 boolean으로 변환시켜주는 옵션입니다.
- tenary는 이진 연산식의 잘못된 값에 대해 null을 반환하는 삼항 표현식으로 변환합니다.
배열로 제공한다면 첫 번째 인자가 autofixing에 사용되므로 순서가 중요합니다.
{
// ...
"react/jsx-no-leaked-render": [<enabled>, { "validStrategies": ["ternary", "coerce"] }]
// ...
}
만약 tenary만 줬다면 어떻게 변할지 예시 또한 친절하게 제공하고 있습니다.
// X
const Component = ({ count, title }) => {
return <div>{count && title}</div>
}
// O
const Component = ({ count, title }) => {
return <div>{count ? title : null}</div>
}
// X
const Component = ({ count, title }) => {
return <div>{!!count && title}</div>
}
// O
const Component = ({ count, title, empty }) => {
return <div>{count ? title : empty}</div>
}
coerce만 줬다면 다음과 같이 autofixing을 제공합니다.
// X
const Component = ({ count, title }) => {
return <div>{count && title}</div>
}
// O
const Component = ({ count, title }) => {
return <div>{!!count && title}</div>
}
// X
const Component = ({ count, title }) => {
return <div>{count ? title : null}</div>
}
// O
const Component = ({ count, title, empty }) => {
return <div>{count ? title : empty}</div>
}
당시에 리뷰에 답변을 달 때는 제가 갖고 있던 eslint 규칙이 { "validStrategies": ["ternary", "coerce"] } 와 같이 제공됐는데 만약 팀에서 가독성을 위해서라고 했을 때 배열의 순서를 바꾸는 방법을 제안했어도 괜찮았을거라는 생각이 드네요.
사용하지 않아야 할 때
Typescript와 같은 정적 언어를 사용한다면, 불리언 조건과 관련된 많은 에러나 경고를 타입 검사를 통해 이미 처리할 수 있기 때문에 이럴 땐 규칙을 꺼도 좋다고 합니다. 그러나 여전히 다른 ESLint 규칙은 코드의 가독성이나 일관성을 강화하는 데 도움을 줄 수 있으므로 저는 키겠습니다.
참조
https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-leaked-render.md
'software engineering > frontend' 카테고리의 다른 글
Zustand Persist (부제: localstorage 쉽게 저장하기) (1) | 2024.02.12 |
---|---|
Next13 import-meta-env 도입기 그리고 주의점 (1) | 2024.02.12 |
React TypeScript Cheatsheet (0) | 2023.12.19 |
UX 라이팅 (0) | 2023.11.19 |
Event Types in React and TypeScript (1) | 2023.11.19 |