컴포넌트의 기능은 단순한 템플릿 이상. 데이터가 주어졌을 때 이에 맞추어 UI를 만들어 주는 것은 물론이고 라이프사이클 API를 이용하여 컴포넌트가 화면에서 나타날 때, 사라질 때 변화가 일어날 때 주어진 작업들을 처리할 수 있으며, 임의 메서드를 만들어 특별한 기능을 붙여줄 수 있다.
이전에는 함수형 컴포넌트를 봤었다. 컴포넌트를 선언하는 방식은 크게 두 가지. 하나는 하수 컴포넌트이고 또 다른 하나는 클래스형 컴포넌트이다.
클래스형 vs 함수형 컴포넌트
[클래스형]
- state 기능
- 라이프 사이클 기능 사용 가능
- 임의의 메서드 정의 가능
🙋 그럼 어느 상황에서 무엇을 사용할까? 🙋
함수 컴포넌트의 장점을 나열해보면
주요한 단점은 state 와 라이프사이클 API 의 사용이 불가능하다는 점. 그러나 이후에 v16.8 업데이트 이후 Hooks라는 기능이 도입되면서 해결되었다. 공식 메뉴얼에서 컴포넌트를 작성 시 함수 컴포넌트와 Hooks를 사용하도록 권장하고 있다.
첫 번째 컴포넌트를 만들어보잣!
컴포넌트를 만들려면 컴포넌트 코드를 선언해야 한다.
요렇게 파일을 하나 만들어주자
화살표 문법을 통해서 생성. 함수형 컴포넌트를 선언 시 function 키워드를 사용하는 것과 화살표 함수를 사용하는 것은 큰 차이 없음. 화살표 함수가 좀 더 간결하기 때문에 사용.
export default Mycomponent;
이 코드는 다른 파일을 import할 때, 위에서 선언한 Mycomponent 클래스를 불러오도록 설정한다.
이번에는 App 컴포넌트에서 Mycomponent 컴포넌트를 불러와서 사용해보자.
매일 프랍프랍?해서 몰랐지만 properties를 줄인 표현으로 컴포넌트의 속성을 설정할 때 사용하는 요소. props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있다.
props를 렌더링할 때 2장에서 배운 것처럼 JSX 내부에서 { } 기호로 감싸주면 된다!
위와 같이 name의 값을 지정해주지 않으면 "안녕, 제 이름은"으로만 나온다. 그러므로 지정을 하지 않았을 때 보여줄 기본값을 설정하는 defaultProps를 알아보자.
리액트 컴포넌트를 사용할 때 컴포넌트 태그 사이의 내용을 보여주는 props가 있는데 바로 children이다.
위 코드에서 MyComponent 태그 사이에 작성한 문다열을 보여주려면 props.children 값을 보여주어야 한다.
주의! 현재 MyComponent에서 작업하고 있다는 것을 까먹지 말자! app.js에서만 바꾸고 이게 왜 안 되지 하지 말고! 감싼 태그를 우선 살펴봐!!!
현재 MyComponent에서 props 값을 조회할 때마다 props.name, props.children과 같이 props. 이라는 키워들르 앞에 붙여주고 있다. 이러한 작업을 더 편하게 하기 위해 ES6의 비구조화 할당 문법을 사용해 내부 값을 바로 추출하는 방법을 알아보자!
요렇게 const {ㅇ, ㅅ } = props 처럼 작성해주면 더 짧은 코드로 사용할 수 있으며, 이렇게 객체에서 값을 추출하는 문법을 비구조화 할당(destructuring assignment)이라고 부른다. 함수의 파라미터에서도 사용할 수 있으며 파라미터가 객체라면 그 값을 바로 비구조화해서 사용하는 것!
요렇게 props를 사용할 때 파라미터 부분에서 비구조화 할당 문법을 사용할 수 있다!!
컴포넌트의 필수 props를 지정하거나 props의 타입을 지정할 때는 propTypes를 사용한다. 컴포넌트의 propTypes를 지정하는 방법은 defaultProp을 설정하는 것과 유사.
이렇게 설정해주면 name 값은 무조건 문자열 현태로 전달해줘야 된다는 것을 의미한다. 값이 나타나기는 했지만 콘솔에는 경고 메세지를 출력해 경고를 출력한다.
propTypes를 지정하지 않았을 때 경고 메세지를 띄워주는 작업을 해보자! propTypes를 지정할 때 뒤에 isRequired를 붙여주면 된다.
default와 propTypes는 필수는 아님! 그러나 프로젝트가 커질수록, 협업을 할 때 해당 컴포넌트가 어떤 props가 필요하진 쉽게 알 수 있어 능률이 올라갈 수 있다!
리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값. props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용할 수 있다. props를 바꾸려면 부모 컴포넌트에서 바꿔줘야 한다.
🙆♀️ 리액트에는 두 가지 종류의 state가 존재한다.
위 파일에서 각 코드가 어떤 역할을 하는지 알아보자. 컴포넌트에 state를 설정할 때는 다음과 같이 constructor 메서드를 작성하여 설정한다.
state 객체 안에는 여러 값이 있을 수 있다.
현재 state 안에 fixedNumber라는 또 다른 값을 추가. 버튼이 클릭될 때 fixedNumber 값은 그대로 두고 number 값만 변화. this.setState 함수는 인자로 전달된 객체 안에 들어 있는 값만 바꿔준다.
앞에서 state의 초깃삾을 지정하기 위해 constructor 메서드를 선언해줬다. 또 다른 방법으로 state를 지정해줄 수도 있다.
이렇게 하면 constructor 메서드를 선언하지 않고도 state 초깃값을 설정할 수 있다!
this.setState를 사용하여 state 값을 업데이트할 때는 상태가 비동적으로 업데이트된다.
요렇게 두 번 호출하면 어떻게 될까? this.setState를 두 번 사용하는 것임에도 불구하고 버튼을 클릭할 때 1씩만 더해진다. this.setState를 사용한다고 해서 state 값이 바로 바뀌지 않기 때문이다.
해결책
this.setState를 사용할 객체 대신에 함수를 인자로 넣어 주는 것. this.setState의 인자로 함수를 넣어 줄 때는 코드를 다음과 같은 형식으로 작성한다.
여기서 prevState는 기존 상태이고, props는 현재 지니고 있는 props를 가리킨다. 만약 업데이트하는 과정에서 props가 필요하지 않으면 생략해도 된다.
화살표 함수에서 값을 바로 반환하고 싶다면 코드 블록 {} 을 생략하면 된다. 예를 들어, 파라미터 a와 b를 받아와서 합을 구해 주는 함수를 작성하고 싶다면 다음과 같이 작성할 수 있다.
const sum = (a,b) => a + b;
setState를 사용하여 값을 업데이트하고 난 다음에 특정 작업을 하고 싶을 때는 setState의 두 번째 파라미터로 콜백(callback)함수를 등록하여 작업을 처리할 수 있다.
리액트 16.8 이전에서는 함수 컴포넌트에서 state를 사용할 수 없었다. 그러나 16.8 이후부터는 useState라는 함수를 사요앟여 함수 컴포넌트에서도 state를 사용할 수 있게 되었다. 사용법은 조금 다르다! 이 과정에서 Hooks가 등장!
배열 비구조화를 알면 useState 사용이 쉽다.
useState 함수의 인자에는 상태의 초기값을 넣어준다. 클래스형 커포넌트에서의 state 초깃값은 객체 형태를 넣어줘야 했지만 useState에서는 반드시 객체가 아니어도 상관 없다. 값의 형태는 자유.
함수를 호출하면 배열이 반환된다. 배열의 첫 번째 원소는 현재 상태이고 두 번째 원소는 상태를 바꾸어주는 함수. 이 함수를 세터(Setter)함수라고 부른다. 그리고 배열 비구조화 할당을 통해 이름을 자유롭게 정해줄 수 있다. 현재 message와 setMessage라고 이름을 설정해주었는디 text와 setText라고 이름을 바꿔줘도 된다.
클래스형 컴포넌트, 함수 컴포넌트든 state를 사용할 때는 주의해야 할 사항이 있다. state 값을 바꿔야 할 때는 setState 혹은 useState를 통해 전달 받은 세터 함수를 사용해야 한다.
요것은 잘못된 코드이다. 배열이나 객체를 업데이트할 때는 배열이나 객체 사본을 만들고 그 사본에 값을 업데이트 한 후, 그 사본의 상태를 setState 혹은 세터 함수를 통해 업데이트 한다.
props와 state는 둘 다 컴포넌트에서 사용하거나 렌더링할 데이터를 담고 있으므로 비슷해 보일 수 있다.
props를 사용한다고 해서 값이 무조건 고정적이지 않다. 부모 컴포넌트의 state를 자식 컴포넌트의 props로 전달하고 => 자식 컴포넌트에서 특정 이벤트가 발생할 때 부모 컴포넌트의 메서드를 호출하면 props도 유동적으로 사용할 수 있다. 이후 만들어 볼 일정 관리 애플리케이션에서 이러한 구조로 프로젝트를 설계하게 된다.