갑자기 이런 의문이 들었다.
리액트 함수형 컴포넌트를 호출해서 사용하지 않고 왜 JSX를 사용하는가? 결국은 똑같은 것 아닌가?
왜냐하면 함수가 리턴하는 값은 얼핏보아 똑같아 보이기 때문이다.
이러한 궁금증을 해결해줄 아티클을 보았다. 원문 링크
function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(c => c + 1)
return <button onClick={increment}>{count}</button>
}
function App() {
const [items, setItems] = useState([])
const addItem = () => setItems(i => [...i, {id: i.length}])
return (
<div>
<button onClick={addItem}>Add Item</button>
<div>{items.map(Counter)}</div>
</div>
)
}
다음과 같은 코드가 있다. 얼핏 보면 잘 동작할 것 같지만, 잘 동작하지 않는다.
map함수에 전달한 컴포넌트가, 콜백으로 쓰이니 정상적으로 동작해야 되지 않는가? 똑같은 코드가 아닌가 싶을 것이다.
그럼, 저 코드를 리팩터링 해보자. Counter
를 inline 해보는 것이다.
function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount(c => c + 1)
return <button onClick={increment}>{count}</button>
}
function App() {
const [items, setItems] = useState([])
const addItem = () => setItems(i => [...i, {id: i.length}])
return (
<div>
<button onClick={addItem}>Add Item</button>
<div>{items.map(function () {
const [count, setCount] = useState(0)
const increment = () => setCount(c => c + 1)
return <button onClick={increment}>{count}</button>
}}</div>
</div>
);
}
여기서 눈치가 빠른사람은 쉽게 알 수 있다. 바로 Hook의 규칙을 위배하고 있었던 것이다.
map 안의 Counter
함수의 useState
가 동적으로 불러지는 것이다.
정확한 동작원리는 모르지만, 공식문서에 따르면 React가 특정 state
가 어떤 useState
호출에 해당하는지 알 수 있는건 React가 호출되는 순서에 의존한다는 것이다.
즉, JSX를 써서 명확하게 컴포넌트임을 알려주어야 한다. 이 동작은 Babel이 도와준다.
차이를 살펴보자.
// Use Counter as function
function App() {
const [items, setItems] = useState([]);
const addItem = () => setItems(i => [...i, {
id: i.length
}]);
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
onClick: addItem
}, "Add Item"), /*#__PURE__*/React.createElement("div", null, items.map(Counter)));
}
// Use Counter as Component(JSX)
function App() {
const [items, setItems] = useState([]);
const addItem = () => setItems(i => [...i, {
id: i.length
}]);
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("button", {
onClick: addItem
}, "Add Item"), /*#__PURE__*/
}
겹치는 코드가 있으므로 알짜배기만 살펴보면..
// Use Counter as function
React.createElement("div", null, items.map(Counter)));
// Use Counter as Component
React.createElement("div", null, items.map(() => React.createElement(Counter, null))));
ReactElement가 전달되냐, 표현식이 전달되냐의 큰 차이가 있다고 본다. 아직은 잘 모르지만 동작원리에서 큰 차이가 있지 않을까?
물론.. 아래 예시처럼 hook이 있지않다면 상관은 없다. 근데… 당연하게도 앞선 사례처럼 예기치 못한 작동방식을 우려할 수 있다.
const Dummy = () => {
return <div>dummy</div>;
}
function App() {
return (
<div>
{Dummy()}
{Dummy()}
{Dummy()}
{Dummy()}
</div>
)
}
https://kentcdodds.com/blog/dont-call-a-react-function-component
https://ko.reactjs.org/docs/hooks-rules.html