만약 어떤 값이 true
일 때 렌더링하고 false일 때는 렌더하지 않는 식으로, 조건에 따라 렌더링을 하거나 하지 않는 경우가 있다. 이때는 삼항연산자
를 사용하지 말고 대신 &&연산자
를 활용하는 것이 좋다.
import React, { useState } from 'react'
export const ConditionalRenderingWhenTrueBad = () => {
const [showConditionalText, setShowConditionalText] = useState(false)
const handleClick = () =>
setShowConditionalText(showConditionalText => !showConditionalText)
return (
<div>
<button onClick={handleClick}>Toggle the text</button>
{/* (역주) 삼항연산자를 사용하면 null 같이 불필요한 코드가 늘어남. */}
{showConditionalText ? <p>The condition must be true!</p> : null}
</div>
)
}
import React, { useState } from 'react'
export const ConditionalRenderingWhenTrueGood = () => {
const [showConditionalText, setShowConditionalText] = useState(false)
const handleClick = () =>
setShowConditionalText(showConditionalText => !showConditionalText)
return (
<div>
<button onClick={handleClick}>Toggle the text</button>
{/* (역주) 삼항연산자를 활용하면 조건별 렌더링을 더 가시적으로 표현할 수 있습니다. */}
{showConditionalText && <p>The condition must be true!</p>}
</div>
)
}
JavaScript에서 truthy, falsy가 모호하기 때문에 && 키워드를 쓸 때 !!를 쓰는 것을 추천하는 의견이 있다. 다음과 같이 작성하면 된다.
{ !!showContitionalText && <p>The condition must be true!</p> }
만약 어떤 값이 true
일 때 A를 렌더링하고 false
일 때는 B를 렌더하지 식으로 조건에 따라 서로 다른 결과를 렌더링해야 하는 경우가 있다. 이때는 삼항연산자
를 쓰는 것이 좋습니다.
import React, { useState } from 'react'
export const ConditionalRenderingBad = () => {
const [showConditionOneText, setShowConditionOneText] = useState(false)
const handleClick = () =>
setShowConditionOneText(showConditionOneText => !showConditionOneText)
return (
<div>
<button onClick={handleClick}>Toggle the text</button>
{/* (역주) 이 경우 && 연산자를 활용하면 불필요하게 코드가 늘어납니다. */}
{showConditionOneText && <p>The condition must be true!</p>}
{!showConditionOneText && <p>The condition must be false!</p>}
</div>
)
}
import React, { useState } from 'react'
export const ConditionalRenderingGood = () => {
const [showConditionOneText, setShowConditionOneText] = useState(false)
const handleClick = () =>
setShowConditionOneText(showConditionOneText => !showConditionOneText)
return (
<div>
<button onClick={handleClick}>Toggle the text</button>
{/* (역주) 삼항연산자를 활용하면 조건별 렌더링을 더 가시적으로 표현할 수 있습니다. */}
{showConditionOneText ? (
<p>The condition must be true!</p>
) : (
<p>The condition must be false!</p>
)}
</div>
)
}
렌더 내부에 로직을 포함하는 것 자체에 대한 반대의견이 있었다. 분기렌더 로직은 렌더 가장 처음이나 외부로 빼내는 것이 가독성에 좋다는 의견이었다.
3. Boolean값을 Props로 넘길 때는 true를 생략하자
따로 값을 할당하지 않아도 prop명
만으로 컴포넌트에 참으로 평가되는 값을 제공할 수 있는 경우가 있다. 이런 경우에 해당 prop
값으로 true
를 굳이 명시할 필요가 없다. 가령 myTruthProp={true}
이런 식으로 작성할 필요는 없다는 뜻이다.
import React from 'react'
const HungryMessage = ({ isHungry }) => (
<span>{isHungry ? 'I am hungry' : 'I am full'}</span>
)
export const BooleanPropBad = () => (
<div>
<span>
<b>This person is hungry: </b>
</span>
{/* (역주) isHungry의 값으로 굳이 true를 명시 */}
<HungryMessage isHungry={true} />
<br />
<span>
<b>This person is full: </b>
</span>
<HungryMessage isHungry={false} />
</div>
)
import React from 'react'
const HungryMessage = ({ isHungry }) => (
<span>{isHungry ? 'I am hungry' : 'I am full'}</span>
)
export const BooleanPropGood = () => (
<div>
<span>
<b>This person is hungry: </b>
</span>
{/* (역주) isHungry의 값으로 굳이 true를 명시하지 않아도 됩니다. */}
<HungryMessage isHungry />
<br />
<span>
<b>This person is full: </b>
</span>
<HungryMessage isHungry={false} />
</div>
)
isHungry에 대한 기본값을 설정하는 방법이 있다는 의견이 있다.
true와 같은 Boolean 값을 생략하기보다 개발자의 의도를 명확하게 하기 위해 명시적으로 true, false 값을 입력해주는 것이 좋다는 의견이 있다.
4. 문자열 값을 Props로 넘길 때는 쌍따옴표를 이용하자
문자열 prop
값은 별도의 중괄호({}, curly brace)나 백틱(``, backticks)없이 그저 쌍따옴표만을 통해서도 전달할 수 있다.
import React from 'react'
const Greeting = ({ personName }) => <p>Hi, {personName}!</p>
export const StringPropValuesBad = () => (
<div>
{/* 문자열 prop값을 중괄호, 백틱, 쌍따옴표, 홑따옴표로 감싸 전달한 사례 */}
<Greeting personName={"John"} />
<Greeting personName={'Matt'} />
<Greeting personName={`Paul`} />
</div>
)
import React from 'react'
const Greeting = ({ personName }) => <p>Hi, {personName}!</p>
export const StringPropValuesGood = () => (
<div>
{/* (역주) 문자열 prop값은 그저 쌍따옴표만으로도 충분히 전달할 수 있습니다. */}
<Greeting personName="John" />
<Greeting personName="Matt" />
<Greeting personName="Paul" />
</div>
)
현업에서 은근히 동료들끼리 코드컨벤션이 안맞았던 부분중 하나였던 것 같다.
5. 인자가 단일 객체 뿐인 이벤트 핸들러 함수는 함수명만 입력하자.
만약 이벤트 핸들러가 오직 Event 객체 하나만을 인자로 받는다면, 그냥 이벤트 핸들러
로 함수명
만을 입력하면 된다. 즉, onChange={e => handleChange(e)}
이라고 쓸 필요없이 그냥 onChange={handleChange}
라고 쓰면 된다는 뜻이다.
import React, { useState } from 'react'
export const UnnecessaryAnonymousFunctionsBad = () => {
const [inputValue, setInputValue] = useState('')
const handleChange = e => {
setInputValue(e.target.value)
}
return (
<>
<label htmlFor="name">Name: </label>
{/* Event 객체 하나만 인자로 받는데 인자를 전달하는 함수형태로 작성 */}
<input id="name" value={inputValue} onChange={e => handleChange(e)} />
</>
)
}
import React, { useState } from 'react'
export const UnnecessaryAnonymousFunctionsGood = () => {
const [inputValue, setInputValue] = useState('')
const handleChange = e => {
setInputValue(e.target.value)
}
return (
<>
<label htmlFor="name">Name: </label>
{/* 그저 Event 객체 하나만 인자로 받는 함수는 함수명만을 입력 */}
<input id="name" value={inputValue} onChange={handleChange} />
</>
)
}
handleChange가 바로 위에서 정의되어 있지만, 혹시라도 스토어의 메소드를 호출하는 경우라면 해당 메소드가 화살표함수로 짜여있지 않거나, 혹은 handleChange의 bind가 제대로 처리되어 있지 않으면 this를 제대로 바인딩하지 못해 의도하지 않은 결과를 낼 수 있지 않나 생각했다. 물론 요즘은 대부분 함수형 컴포넌트로 개발하고 있고 화살표 함수가 대중화된만큼 과한 걱정이라 생각이 든다.
어떤 컴포넌트(B)에 prop
으로 또 다른 컴포넌트(A)를 전달하는 경우에, 전달되는 컴포넌트(A)가 아무 props
를 받지 않는다면 굳이 함수로 감쌀 필요없이 컴포넌트명만을 전달
해도 좋다.
import React from 'react'
const CircleIcon = () => (
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
)
const ComponentThatAcceptsAnIcon = ({ IconComponent }) => (
<div>
<p>Below is the icon component prop I was given:</p>
<IconComponent />
</div>
)
export const UnnecessaryAnonymousFunctionComponentsBad = () => (
{/* (역주) CircleIcon은 아무런 인자를 받지 않는데도 함수로 감싸서 전달 */}
<ComponentThatAcceptsAnIcon IconComponent={() => <CircleIcon />} />
)
좋은 사례
import React from 'react'
const CircleIcon = () => (
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
)
const ComponentThatAcceptsAnIcon = ({ IconComponent }) => (
<div>
<p>Below is the icon component prop I was given:</p>
<IconComponent />
</div>
)
export const UnnecessaryAnonymousFunctionComponentsGood = () => (
{/* 아무런 인자를 받지 않는 CircleIcon를 함수로 싸지 않고 컴포넌트명으로 전달 */}
<ComponentThatAcceptsAnIcon IconComponent={CircleIcon} />
)
undefined props
는 제외된다. 만약 어떤 props
가 undefined
로 제공되어도 컴포넌트 작동에 문제가 없다면, props
값으로 undefined
를 전달하는 것에 대한 대비책을 걱정할 필요는 없다.
import React from 'react'
const ButtonOne = ({ handleClick }) => (
<button onClick={handleClick || undefined}>Click me</button>
)
const ButtonTwo = ({ handleClick }) => {
const noop = () => {}
return <button onClick={handleClick || noop}>Click me</button>
}
export const UndefinedPropsBad = () => (
<div>
<ButtonOne />
<ButtonOne handleClick={() => alert('Clicked!')} />
<ButtonTwo />
<ButtonTwo handleClick={() => alert('Clicked!')} />
</div>
)
import React from 'react'
const ButtonOne = ({ handleClick }) => (
<button onClick={handleClick}>Click me</button>
)
export const UndefinedPropsGood = () => (
<div>
<ButtonOne />
<ButtonOne handleClick={() => alert('Clicked!')} />
</div>
)
만약 새로운 상태가 이전의 상태값에 의존한다면, 이전 state
값을 이용한 함수(updater
함수)를 전달해야 한다. react
상태 갱신은 일괄적
으로 이뤄지므로 이런 식으로 갱신하지 않으면 예상치 못한 결과가 나올 수 있다.
아래 예시를 직접 구현해보고 Toggle button state 2 times
버튼을 눌러본다면 나쁜 예시에서 의도한대로 상태갱신이 이뤄지지 않음을 알 수 있을 것이다.
import React, { useState } from 'react'
export const PreviousStateBad = () => {
const [isDisabled, setIsDisabled] = useState(false)
// (역주) 이전 값에 의존하는 상태갱신 함수에 갱신결과값만을 전달하면
const toggleButton = () => setIsDisabled(!isDisabled)
// (역주) 이 함수의 결과가 정상적으로 작동하지 않음을 알 수 있습니다.
const toggleButton2Times = () => {
for (let i = 0; i < 2; i++) {
toggleButton()
}
}
return (
<div>
<button disabled={isDisabled}>
I'm {isDisabled ? 'disabled' : 'enabled'}
</button>
<button onClick={toggleButton}>Toggle button state</button>
<button onClick={toggleButton2Times}>Toggle button state 2 times</button>
</div>
)
}
import React, { useState } from 'react'
export const PreviousStateGood = () => {
const [isDisabled, setIsDisabled] = useState(false)
// (역주) 상태갱신 함수의 인자로 이전 값을 인자로 하는 updater 함수를 전달하면
const toggleButton = () => setIsDisabled(isDisabled => !isDisabled)
// (역주) 아래 함수가 의도한대로 동작함을 알 수 있습니다.
const toggleButton2Times = () => {
for (let i = 0; i < 2; i++) {
toggleButton()
}
}
return (
<div>
<button disabled={isDisabled}>
I'm {isDisabled ? 'disabled' : 'enabled'}
</button>
<button onClick={toggleButton}>Toggle button state</button>
<button onClick={toggleButton2Times}>Toggle button state 2 times</button>
</div>
)
}
updater 함수를 전달해야 하는 것이 기본 스펙 문서에도 작성되어 있음에도 불구하고 간과하기 쉬운 부분이었다.
아래 내용은 React의 필수법칙은 아니지만 클린코드를 짜는 좋은 방법이 될 것이다.
해당 문제에 대해서는 자바스크립트 뿐만 아니라 어떤 프로그래밍 언어에서든지 말이다.