먼저 두 개의 버튼을 만들어 보자.
function SaveBtn () {
return < button > Save </ button >;
}
function ConfirmBtn () {
return < button > Confirm </ button >;
}
function App () {
return (
< div >
< SaveBtn />
< ConfirmBtn />
</ div >
)
}
const root = document . getElementById ( " root " ) ;
ReactDOM . render ( < App />, root ) ;
위와 같은 상황에서 만약 버튼에 같은 style을 추가하고 싶다면 복사 붙여넣기로 각각 추가해줘야 할 것이다.
그런데 각각 style을 추가해주는 대신 같은 설정들을 넘겨줄 수 있는 컴포넌트를 한 개만 만들어 재사용한다면 훨씬 효율적일 것이다.
다음과 같이 바꿔보자.
function Btn ({text, big} ) {
return < button style ={{
background : " tomato ", color : " white ", padding : " 10px, 20px ", border : 0 , borderRadius : 10, fontSize : big ? 18 : 16 }}>
{ text } </ button >;
}
function App () {
return (
< div >
< Btn text ="Save " size = {true} />
< Btn text =" Confirm " size = {false} />
</ div >
)
}
const root = document . getElementById ( " root " ) ;
ReactDOM . render ( < App />, root ) ;
1. Props
Props는 부모 컴포넌트에서 자식 컴포넌트에 데이터를 보낼 수 있게 해주는 방법이다.
함수 Btn에 원하는 스타일을 적용시켜 놓고 App에서 Btn을 호출하여 text와 big을 넣는 것이다.
Btn 함수는 다양한 argument를 받으며 argument의 이름은 마음대로 지을 수 있다.
이러한 다양한 argument를 property 또는 props라고 부른다.
즉, 컴포넌트에 보내지는 argument인 것이다.
리액트가 하는 작업은 Btn함수를 호출하여 props들을 첫 번째 인자로 넣는 것이다.
그런데 App 안에서 불러온 Btn에 prop을 추가하는 것은 이벤트 리스너가 아니며 그냥 Btn 함수에 들어가는 prop일 뿐이다.
이벤트 리스너는 Btn 함수 자체에 넣어줘야 한다.
function Btn ({ text , onClick }) {
return < button onClick ={ onClick } style ={{
background : " tomato ", color : " white ", padding : " 10px 20px ", border : 0 , borderRadius : 10 }}>
{ text } </ button >;
}
function App () {
const [ value , setValue ] = React . useState ( " Save " ) ;
const changeValue = () => setValue ( " revert changes " ) ;
return (
< div >
< Btn text ={ value } onClick ={ changeValue } />
< Btn text =" Confirm "/>
</ div >
)
}
const root = document . getElementById ( " root " ) ;
ReactDOM . render ( < App />, root ) ;
위와 같이 App 안 Btn에서의 onClick은 그냥 prop의 이름일 뿐이며 이를 Btn 함수가 인자로 받아 onclick 이벤트 리스너로 실행시켜주는 것이다.
만약 Btn 함수 안에서 onclick 이벤트 리스너가 없고 App 안 Btn에만 onclick이 있다면 이는 적용되지 않을 것이다.
그저 prop이기 때문이다.
2. Memo
그런데 버튼을 클릭하면 value가 바뀌고 re-render가 되는데 변화가 없었던 confirm 버튼도 같이 re-render가 된다.
부모 컴포넌트의 state를 변경하면 당연히 그 자식 컴포넌트도 re-render가 일어나기 때문이다.
그런데 이는 어플리케이션이 느려지는 원인이 될 수 있다.
prop가 변경되지 않아서 불필요한 re-render가 없길 원하는 컴포넌트가 있다면 React Memo를 사용해보자.
https://ko.reactjs.org/docs/react-api.html#reactmemo
React 최상위 API – React
A JavaScript library for building user interfaces
ko.legacy.reactjs.org
React.memo는 고차 컴포넌트(Higher Order Component)입니다. 컴포넌트가 동일한 props로 동일한 결과를 렌더링해낸다면, React.memo를 호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다.
function Btn ({ text , onClick }) {
return < button onClick ={ onClick } style ={{
background : " tomato ", color : " white ", padding : " 10px 20px ", border : 0 , borderRadius : 10 }}>
{ text } </ button >;
}
const MemorizedBtn = React . memo ( Btn ) ;
function App () {
const [ value , setValue ] = React . useState ( " save changes " ) ;
const changeValue = () => setValue ( " revert changes " ) ;
return (
< div >
< MemorizedBtn text ={ value } onClick ={ changeValue } />
< MemorizedBtn text =" confirm "/>
</ div >
)
}
const root = document . getElementById ( " root " ) ;
ReactDOM . render ( < App />, root ) ;
위와 같은 코드에서는 save changes 버튼을 눌러도 옆에 있는 confirm 버튼은 re-render가 되지 않는다.
prop가 변경되지 않는다면, 즉 state가 변경되지 않았다면 re-render하지 않도록 하여 불필요한 re-render가 일어나는 것을 막을 수 있다.
3. Prop Types
만약 각 props에 특정 타입이 입력되길 원하다면 proptype을 설치하여 사용해보자.
function Btn ({ text , fontSize }) {
return < button style ={{
background : " tomato ", color : " white ", padding : " 10px 20px ", border : 0 , borderRadius : 10 , fontSize }}>
{ text } </button>;
}
Btn .propTypes = {
text : PropTypes.string, fontSize : PropTypes.number
};
function App () {
return (
<div>
<Btn text= " save " fontSize ={ 18 } />
< Btn text =" confirm " fontSize ={ 12 } />
</ div >
)
}
const root = document . getElementById ( " root " ) ;
ReactDOM . render ( < App />, root ) ;
위의 코드는 text에는 string, fontSize에는 number가 들어와야 함을 정해주는 것이다.
다른 타입이 들어가면 에러는 안나지만 콘솔창에서 경고가 뜨는 것을 확인할 수 있다.
만약 뒤에 .isRequired를 붙인다면 해당 prop는 반드시 가지고 있어야 함을 의미하며 해당 prop이 빠져있다면 경고창이 뜨는 것을 확인할 수 있다.
Btn .propTypes = {
text : PropTypes.string.isRequired, fontSize : PropTypes.number.isRequired
};
prop의 기본값을 설정할 수도 있다.
다음과 같이 두번째 버튼에는 fontSize prop이 없으므로 Btn에서 정의한 fontSize인 16이 적용된다.
function Btn ({ text , fontSize = 16 }) {
return < button style ={{
background : " tomato ", color : " white ", padding : " 10px 20px ", border : 0 , borderRadius : 10 , fontSize }}>
{ text } </button>;
}
Btn .propTypes = {
text : PropTypes.string.isRequired, fontSize : PropTypes.number
};
function App () {
return (
<div>
<Btn text= " save " fontSize ={ 18 } />
< Btn text =" confirm " />
</ div >
)
}
const root = document . getElementById ( " root " ) ;
ReactDOM . render ( < App />, root ) ;