리액트를 사용한 애플리케이션의 인터페이스를 설계할 때 사용자가 볼 수 있는 요소는 컴포넌트로 구성되어 있다.
컴포넌트는 단순한 템플릿 이상으로, UI를 만들어주는 것은 물론, 컴포넌트가 화면에 나타날 때, 사라질 때, 변화가 일어날 때 주어진 작업을 처리할 수 있고, 특별한 기능을 만들어 붙일 수도 있다.
컴포넌트는 함수형과 클래스형, 두 가지 방식으로 작성할 수 있다.
function App() {
const name = "리액트";
return (
<>
<h1>{name}</h1>
</>
);
}
함수형 컴포넌트는 클래스형에 비해 선언하기가 매우 편하고, 메모리를 덜 사용한다. 그리고 프로젝트 빌드 후 배포할 때에도 파일 크기가 더 작다. (하지만 사실상 별 차이 없다.)
그리고 함수형 컴포넌트의 단점은 state와 라이프사이클 API 사용이 불가능하다는 점이었는데 리액트 v16.8 업데이트 이후 Hooks
기능이 도입되면서 해결되었다.
리액트 공식 매뉴얼에서는 컴포넌트를 작성할 때 함수형 컴포넌트+Hooks를 사용하도록 권장하고 있다.
class App extends Component {
render() {
const name = "리액트";
return <h1>{name}</h1>;
}
}
클래스형 컴포넌트의 경우, 이후 배울 state 기능과 라이프사이클 기능을 사용할 수 있고, 임의 메서드를 정의할 수 있다.
내가 MyComponent.js
컴포넌트를 작성했다고 했을 때 이를 다른 컴포넌트에서 사용하고 싶다면, 먼저 MyComponent.js
파일의 하단에
export default MyComponent;
를 작성해 주고, MyComponent
를 가져오고 싶은 코드의 상단에
import Mycomponent from `./MyComponent`;
와 같이 작성할 수 있다. 그리고 불러와서 사용할 수 있다.
const App = () => (
return <MyComponent />
)
화살표 함수(arrow function)은 ES6 문법에서 함수를 표현하는 새로운 방식이다. 이 문법은 주로 함수를 파라미터로 전달할 때 유용하다.
setTimeout(function() {
console.log("hello");
}, 1000);
setTimeout(() => {
console.log("hello")
}, 1000);
두 함수는 비슷해 보이지만, 조금 다르다. 다음 코드를 보면 :
import React from "react";
function BlackDog() {
this.name = "흰둥이";
return {
name: "검둥이",
bark: function() {
console.log(this.name + ': 멍멍!');
}
}
}
function WhiteDog() {
this.name = "흰둥이";
return {
name: "검둥이",
bark: () => {
console.log(this.name + ': 멍멍!');
}
}
}
const blackDog = new BlackDog();
blackDog.bark(); // 검둥이 : 멍멍!
const whiteDog = new WhiteDog();
whiteDog.bark(); // 흰둥이 : 멍멍!
서로 가리키고 있는 this
값이 다르다. 일반 function 함수는 자신이 종속된 개체
를 가리키고, 화살표 함수는 자신이 종속된 인스턴스
를 가리킨다.
props는 properties의 줄임 표현이다. 컴포넌트의 속성을 설정할 때 사용한다.
props 값은 함수의 파라미터로 받아 와서 사용할 수 있다.
const MyComponent = (props) => {
return <div>제 이름은 {props.name}입니다.</div>;
};
const App = () => {
return <MyComponent name="React" />;
};
만약 props 값을 따로 지정하지 않았을 때 보여줄 기본값을 설정하는 defaultProps
가 있다.
defaultProps
는 다음과 같이 설정한다.
const MyComponent = (props) => {
return (
<div>{props.name}</div>
)
}
MyComponent.defaultProps = {
name: "기본값"
};
태그 사이의 내용을 보여주는 props가 children
이다.
const MyComponent = (props) => {
return (
<div>
<h1>{props.name}</h1>
<h2>{props.children}</h2>
</div>
);
};
const App = () => {
return <MyComponent>리액트</MyComponent>;
};
<h2>
태그에 '리액트' 가 나타나게 된다.
const MyComponent = (props) => {
const {name, children} = props;
return (
<div>
<h1>{name}</h1>
<h2>{children}</h2>
</div>
);
};
객체에서 값을 추출하는 문법을 비구조화 할당(destructuring assignment) 이라고 하며, 구조 분해 문법이라고도 불린다.
함수의 파라미터 부분에서도 사용할 수 있다.
const MyComponent = ({name, children}) => {
return (
<div>
<h1>{name}</h1>
<h2>{children}</h2>
</div>
);
};
필수로 지정해 주어야 하는 props나 props의 type을 지정할 때에 propTypes를 사용한다.
import propTypes from 'prop
const MyComponent = ({name, children}) => {
return (
<div>
<h1>{name}</h1>
<h2>{children}</h2>
</div>
);
};
MyComponent.propTypes = {
name: propTypes.string
}
name을 문자열(string) 형태로 전달해야 한다는 것을 의미한다.
타입명 뒤에 .isRequired
를 붙이면 필수 props로 지정할 수 있다.
state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다.
props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정해 주는 값이며 컴포넌트 자신은 props를 읽기 전용으로만 사용할 수 있고 변경할 수 없다.
리액트에는 두 가지 종류의 state가 있는데, 하나는 클래스형 컴포넌트가 지니고 있는 stat이고 다른 하나는 함수형 컴포넌트에서 useState
라는 함수를 통해 사용하는 state이다.
다음과 같이 코드를 작성한다.
export default class Counter extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
};
}
render() {
const { number } = this.state;
return (
<div>
<h1>{number}</h1>
<button
onClick={() => {
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
컴포넌트에 state를 설정하는 부분이다.
constructor(props) {
super(props);
this.state = {
number: 0,
};
}
위와 같이 constructor 메서드를 작성하고, 안에 반드시 super(props)
를 호출해 주어야 한다. 이 함수가 호출되면 현재 클래스형 컴포넌트가 상속하고 있는 (extends) 리액트의 Component 클래스가 지닌 생성자 함수를 호출해 준다.
그 다음에는 this.state 값에 초기값을 설정해 준다.
컴포넌트의 state는 객체 형식이어야 한다.
render() {
const { number } = this.state;
return (
<div>
<h1>{number}</h1>
<button
onClick={() => {
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
render 함수에서 현재 state를 조회할 때에는 this.state
를 지정하면 된다.
그리고 버튼의 onClick 이벤트로 this.setState
함수를 사용했는데, 이 함수가 state 값을 바꿀 수 있게 해 준다.
잘 동작하고 있는 모습.
export default class Counter extends Component {
state = {
number: 0,
fixedNumber: 0
};
render() {
const { number } = this.state;
return (
<div>
<h1>{number}</h1>
<button
onClick={() => {
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
위와 같이 state를 작성해 주는 것이 더 간결하다.
onClick={() => {
this.setState(
{
number: number + 1,
},
() => {
console.log("방금 setState가 호출되었습니다.");
console.log(this.state);
}
);
}}
setState
의 두 번째 파라미터로 콜백 함수를 등록하여 작업을 처리할 수 있다.
16.8 이후부터 useState
함수를 이용하여 함수형 컴포넌트에서도 state를 사용할 수 있게 되었다.
이 과정에서 Hooks
라는 것을 사용하게 된다. (챕터 8 내용)
const Say = () => {
const [message, setMessage] = useState('');
const onClickEnter = () => {
setMessage("안녕하세요");
};
return (
<div>
<h1>{message}</h1>
<button onClick={onClickEnter}>입장</button>
</div>
);
}
const [message, setMessage] = useState('');
부분을 잘 보자.
함수를 호출하면 배열이 반환되는데, 배열의 첫 번째 원소는 현재 상태이고 두 번째 원소는 상태를 바꾸어 주는 함수이다. 이 함수를 세터(setter) 함수라고 한다.
이름은 자유롭게 지을 수 있는데 세터 함수는 set+(변수명)으로 짓는 것이 일반적이다.
버튼을 클릭하면 onClickEnter
함수, 곧 setMessage
함수가 호출되어 message 값을 '안녕하세요' 로 바꾸고, 곧이어 h1 태그에서 화면에 나타나게 된다.
state 값을 바꾸어야 할 때는 setState
혹은 useState
를 통한 세터 함수를 사용해야 한다.
const [object, setObject] = useState({a: 1, b: 1});
object.b = 2;
위와 같은 코드는 잘못된 코드이다.
컴포넌트를 만들고, 내보내고 불러오는 (export, import) 방법과 props, state 사용방법을 배웠다.
또한 function형 함수와 화살표 함수의 문법과 차이에 대해서 알게 되었다.