keyword
props, memo, React.memo(), propTypes, isRequired, PropTypes.oneOfType([,]), defaultProps
Props
부모컴포넌트에서 자식컴포넌트로 데이터를 보낼 수 있게 해주는 일종의 방식이자 argument의 역할
이전 예제인 converters에서는 자식컴포넌트(MinutesToHours, KmToMiles)는 부모컴포넌트의 데이터를 필요로 하지 않고 독립적으로 존재 가능했음.
🌼 이번 포스트에서는,
props
는 객체
임.(props) - {props.속성}
또는 ({속성}) - {속성}
으로 사용 가능React.memo()
로 모든 자식컴포넌트의 불필요한 자동 리렌더링 방지 및 성능 최적화 가능PropTypes
으로 props 데이터타입값 설정 및 에러 통보 가능isRequired
추가하면 props 필수값으로 설정 가능PropTypes.oneOfType([,])
로 설정하면, 여러 데이터값 중 하나면 만족으로 설정 가능defaultProps
으로 props 초기값 설정 가능 예제
configuring text를 해보자!
1. 기본 버튼 2개 만들기
<script type="text/babel">
function SaveBtn() {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>Save Changes</button>
)
}
function ConfirmBtn() {
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>Confirm</button>
)
}
function App() {
return (
<div>
<SaveBtn />
<ConfirmBtn />
</div>
)
};
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
부모컴포넌트 -> 자식컴포넌트로
props 통해 String (text) 보내기
2. props 보내기 - 'text' 개별화 커스터마이징
console.log(props) 찍어보면, props가 객체로 넘겨지는데, 이때 커스터마이징한 속성들은 객체의 첫 인자로 들어감.
개별 text props 만들어 주기
<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>
props 객체로 전달해서 UI 출력
function Btn(props) {
console.log(props) // Object
return (
<button
style={{
backgroundColor: "tomato",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{props.text}</button>
)
}
전체코드
function Btn( props ) {
console.log(props) // Object
return (
<button
style={{
//backgroundColor: "/tomato",
backgroundColor: props.important? "tomato":"lightblue",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{props.text}</button>
)
}
function App() {
return (
<div>
<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>
</div>
)
};
부모컴포넌트 -> 자식컴포넌트로
props 통해 true/false 보내기
3. props 보내기 - 다른 방식으로 보내기, T/F도 추가
📌 props는 Object라 {}로 받을 수 있음.
function Btn({ text, important })
<button
style={{
//backgroundColor: "/tomato",
backgroundColor: important? "tomato":"lightblue",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{text}
</button>
📌 String뿐만 아니라, true/false도 props로 보내기 가능
<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>
전체 코드
function Btn({ text, important }) {
//console.log(props) // Object
return (
<button
style={{
//backgroundColor: "/tomato",
backgroundColor: important? "tomato":"lightblue",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{text}</button>
)
}
function App() {
return (
<div>
<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>
</div>
)
};
최종 코드
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.17.6/babel.min.js"></script>
<script type="text/babel">
function Btn({ text, important, backgroundColor }) {
//console.log(props) // Object
return (
<button
style={{
//backgroundColor: "/tomato",
backgroundColor: important? "tomato": "lightblue",
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{text}</button>
)
}
function App() {
return (
<div>
<Btn text="Save Change" important={true}/>
<Btn text="Confirm" important={false}/>
</div>
)
};
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</body>
</html>
부모컴포넌트 -> 자식컴포넌트로
props 통해 style 설정하기
backgroundColor, fontSize 개별 설정
backgroundColor
} 로 안해줌! backgroundColor
만 해도 됨.backgroundColor
<Btn text="Save Change" backgroundColor="lightblue"/>
<Btn text="Confirm" backgroundColor="tomato"/>
backgroundColor
backgroundColor
function Btn({ text , changeValue, backgroundColor}) {
return (
<button
onClick={changeValue}
style={{
backgroundColor,
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{text}</button>
)
}
fontSize
<Btn text={value} changeValue={chagneValue} backgroundColor="lightblue" fontSize={20}/>
fontSize
fontSize
function Btn({ text , changeValue, backgroundColor, fontSize}) {
return (
<button
onClick={changeValue}
style={{
backgroundColor,
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10,
fontSize
}}>{text}</button>
)
}
부모컴포넌트 -> 자식컴포넌트로
props 통해 fucntion 함수 보내기
📌 onClick은 이벤트리스너 (X) 그저 하나의 prop 일뿐!
기타: (최적화 관련) React.memo()
기능: 불필요한 리렌더링 제한, Memorizing
👉 부모컴포넌트에서, props가 (상태)변화하지 않은 자식컴포넌트의 불필요한 렌더링 방지하기.
👉 props가 변화한 자식컴포넌트만 렌더링 시키기
불필요한 re-render는 React.memo()로 관리할 수 있음
문제
자식컴포넌트에 불필요한 렌더링이 발생할 수도 있음해결
React.memo()로 prop의 변경이 일어난 해당 자식컴포넌트만 리렌더링 시켜줄 수 있음. 장점
부모 컴포넌트가 아주 많은 자식컴포넌트를 갖고 있는 경우 사용하면 성능과 속도 최적화에 유용.❗리액트 개발을 하실 때, useCallback, useMemo, React.memo 는 컴포넌트의 성능을 실제로 개선할수있는 상황에서만 사용하기
❗렌더링 최적화 하지 않을 컴포넌트에 React.memo 를 사용하는것은, 불필요한 props 비교만 하는 것이기 때문에 실제로 렌더링을 방지할수있는 상황이 있는 경우에만 사용하기
예제 설명
👉 따라서, state랑 연결된 props가 변경되는 첫번째 자식컴포넌트 버튼은 리렌더링 업데이트 새로고침이 되어야 하겠지만,
👉 두번째 자식컴포넌트 버튼은 변경된 것도 없는데 굳이 리렌더링 될 필요가 없는 것임.
📌 기본적으로 리액트는 부모컴포넌트에 상태변화가 있으면, 모든 자식컴포넌트들도 리렌더링 됨.
추가
React.memo()
컴포넌트가 React.memo()로 wrapping 될때, React는 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing)함.
그리고 다음 렌더링이 일어날 때 props가 같다면, React는 메모이징(Memoizing)된 내용을 재사용함.
예제
React.memo(자식컴포넌트)
const MemorizedBtn = React.memo(Btn)
const [value, setValue] = React.useState("Save Change")
const chagneValue = () => {
setValue("Revert Changes")
}
<MemorizedBtn text={value} changeValue={chagneValue} />
<MemorizedBtn text="Confirm" />
React.memo(자식컴포넌트)해주면, 모든 자식들을 일괄 리렌더링 업데이트가 아니라, 상태변화된 자식컴포넌트만 리렌더링 업데이트해줌
전체코드
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.17.6/babel.min.js"></script>
<script type="text/babel">
function Btn({ text , changeValue, backgroundColor}) {
//console.log(props) // Object
console.log(text, "was rendered")
return (
<button
onClick={changeValue}
style={{
backgroundColor,
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10
}}>{text}</button>
)
}
const MemorizedBtn = React.memo(Btn)
function App() {
const [value, setValue] = React.useState("Save Change")
const chagneValue = () => {
setValue("Revert Changes")
}
return (
<div>
<MemorizedBtn text={value} changeValue={chagneValue} backgroundColor="lightblue"/>
<MemorizedBtn text="Confirm" backgroundColor="tomato"/>
</div>
)
};
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</body>
</html>
Props Types
문제
컴포넌트가 너무 많은 props를 갖는다면?
리액트는 props 작성시 기본 문법에만 맞으면, 타입값 에러가 있어도, UI나 콘솔창에 에러를 알려주지 않음.
해결
: propTypes
사용
각 props별 타입 입력값 지정, 검토 후 에러 통보
https://unpkg.com/prop-types@15.7.2/prop-types.js
Btn.propTypes={
text: PropTypes.string.isRequired,
backgroundColor: PropTypes.string.isRequired,
fontSize: PropTypes.number, // optional
changeValue: PropTypes.func, // optional
value1: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired// 둘중 하나 예시 // optional
disabled: PropTypes.bool // boolean // optional
}
위 코드의 위치는, 컴포넌트 안에 종속되지 않고 독립적으로 씀.
propTypes 사용하면, 만약 props 값 입력시 타입에러가 발생하면, 에러표시로 알려줌
.isRequired를 붙이면 필수로 해당 prop 항목이 있어야함을 의미. 없으면 에러 발생함.
그 외의 경우에는, optional임. props가 등록되지 않아도 에러발생하지 않음.
PropTypes 종류 및 디폴트 기본값 설정 : 👉클릭
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// prop가 특정 JS 형식임을 선언할 수 있습니다.
// 이것들은 기본적으로 모두 선택 사항입니다.
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 랜더링 될 수 있는 것들은 다음과 같습니다.
// 숫자(numbers), 문자(strings), 엘리먼트(elements), 또는 이러한 타입들(types)을 포함하고 있는 배열(array) (혹은 배열의 fragment)
optionalNode: PropTypes.node,
// React 엘리먼트.
optionalElement: PropTypes.element,
// React 엘리먼트 타입 (ie. MyComponent)
optionalElementType: PropTypes.elementType,
// prop가 클래스의 인스턴스임을 선언할 수 있습니다.
// 이 경우 JavaScript의 instanceof 연산자를 사용합니다.
optionalMessage: PropTypes.instanceOf(Message),
// 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있습니다.
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 여러 종류중 하나의 종류가 될 수 있는 객체
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 특정 타입의 행렬
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 특정 타입의 프로퍼티 값들을 갖는 객체
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 특정 형태를 갖는 객체
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 추가 프로퍼티에 대한 경고가 있는 객체
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 위에 있는 것 모두 `isRequired`와 연결하여 prop가 제공되지 않았을 때
// 경고가 보이도록 할 수 있습니다.
requiredFunc: PropTypes.func.isRequired,
// 모든 데이터 타입이 가능한 필수값
requiredAny: PropTypes.any.isRequired,
// 사용자 정의 유효성 검사기를 지정할 수도 있습니다.
// 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
// `oneOfType`안에서는 작동하지 않으므로 `console.warn` 혹은 throw 하지 마세요.
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// `arrayOf` 와 `objectOf 에 사용자 정의 유효성 검사기를 적용할 수 있습니다.
// 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
// 유효성 검사기는 배열(array) 혹은 객체의 각 키(key)에 대하여 호출될 것입니다.
// 유효성 검사기의 첫 두 개의 변수는 배열 혹은 객체 자신과 현재 아이템의 키입니다.
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
props 초기값 설정
function Btn({ text, fontSize = 50})
defaultProps
MemorizedBtn.defaultProps={
fontSize: 100
}
전체 코드 (React.memo()까지 적용 완료)
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.17.6/babel.min.js"></script>
<script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
<script type="text/babel">
function Btn({ text , changeValue, backgroundColor, fontSize}) {
//console.log(props) // Object
console.log(text, "was rendered")
return (
<button
onClick={changeValue}
style={{
backgroundColor,
color: "white",
padding: "10px 20px",
border:0,
borderRadius:10,
fontSize
}}>{text}</button>
)
}
const MemorizedBtn = React.memo(Btn)
MemorizedBtn.propTypes={
text: PropTypes.string.isRequired,
backgroundColor: PropTypes.string.isRequired,
fontSize: PropTypes.number, // optional
changeValue: PropTypes.func // optional
}
MemorizedBtn.defaultProps={
fontSize: 100
}
function App() {
const [value, setValue] = React.useState("Save Change")
const chagneValue = () => {
setValue("Revert Changes")
}
return (
<div>
<MemorizedBtn text={value} changeValue={chagneValue} backgroundColor="lightblue" fontSize={20}/>
<MemorizedBtn text="Continue" backgroundColor="tomato"/>
</div>
)
};
const root = document.getElementById("root");
ReactDOM.render(<App />, root);
</script>
</body>
</html>
<참고>
https://ko.reactjs.org/tutorial/tutorial.html
PropTypes: https://ko.reactjs.org/docs/typechecking-with-proptypes.html
<더 알아보기>
React.memo(): https://ko.reactjs.org/docs/react-api.html#reactmemo
https://react.vlpt.us/basic/19-React.memo.html