import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Blocks from './chapter_15/Blocks';
ReactDOM.render(
<React.StrictMode>
<Blocks />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Provider로 하위 컴포넌트에서 theme과 toggleTheme을 사용 가능하도록 만든다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import DarkOrLight from './chapter_14/DarkOrLight';
ReactDOM.render(
<React.StrictMode>
<DarkOrLight />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
- 영어 뜻 : 구성 - 여러개의 컴포넌트를 합쳐서 새로운 컴포넌트를 만드는 것 - 그래서 구성 보다는 합성이라는 단어가 어울림
조합 방법
Containment
- 담다, 포함하다 - 하위 컴포넌트를 포함하는 형태의 합성 방법 - SideBar나 Dialog 같은 Box 형태의 컴포넌트는 자신의 하위 컴포넌트를 미리 알 수 없다. - children이라는 prop을 사용해서 조합
children 이라는 prop은 리액트에서 기본적으로 제공한다. 이것을 사용하면 해당 컴포넌트의 하위 컴포넌트가 모두 children으로 들어오게 된다.이전에 createElement를 배웠을 때, ...children이 하위 컴포넌트라는 것을 배웠었다.FancyBorder 컴포넌트 안에 있는 모든 JSX 태그는 children으로 전달됨!!
여러개의 children 집합이 필요한 경우 : 별도로 props를 정해서 각각 원하는 컴포넌트를 넣어주면 된다.
다음은 화면을 왼쪽과 오른쪽으로 분할해서 보여주는 역할을 하는 SplitPane 컴포넌트이다.
App 이라는 컴포넌트에서는 SplitPane 컴포넌트를 사용하고 있는데, 여기서 left, right라는 두개의 props를 정의해서 그 안에 각각 다른 컴포넌트를 넣어주고 있다.
SplitPane 에서는 이 left, right 라는 props를 이용해서 각각 왼쪽과 오른쪽 화면을 분리해서 렌더링하여 보여준다.
이 처럼 여러개의 children 집합이 필요한 경우에는 별도의 props를 정의해서 사용하면 된다.
Specialization
- 전문화, 특수화
- 예) Welcome Dialog는 Dialog의 특별한 케이스다.
- 범용적인 개념을 구별이 되게 구체화 하는 것
- 기존의 객체지향 언어에서는 상속을 사용하여 Specialization을 구현한다. 그러나!BUT!
- React에서는 합성(Composition)을 사용하여 Specialization을 구현한다.
Dialog라는 범용적인 컴포넌트와 WelcomeDialog라는 특별한 컴포넌트
제목과 메세지를 WelcomeDialog에서 정의해서 제목과 메세지를 어떻게 사용하냐에 따라 인삿말 다이어로그가 되거나 경고 다이어로그가 된다.
Containment 와 Specialization을 같이 사용하기
children prop 을 통해 Dialog 하위 태그들을 Containment 방식 사용해서 불러 올 수 있으며, title 과 message prop으로 제목과 메세지 정의해서 특수화하여 사용하면 Specialization 역시 구현할 수 있다.
Card 컴포넌트는 하위 컴포넌트를 감싸서 카드 형태로 보여준다. Containment 와 Specialization 두가지 합성 방법을 모두 사용. children 부분이 Containment 부분이고, title과 backgroundColor 부분이 Specialization 부분이다.
ProfileCard 컴포넌트에서 Card 컴포넌트를 사용하여 타이틀에 이름을 넣고, 배경색깔 역시 넣는다.
children 으로는 간단한 자기소개 글을 넣었다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ProfileCard from './chapter_13/ProfileCard';
ReactDOM.render(
<React.StrictMode>
<ProfileCard />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
인덱스에서 렌더링하고 터미널에서 실행하면
제목과 배경색, 그리고 children이 합성되어 하나의 카드로 보여주는 것을 확인할 수 있다.
상위 컴포넌트 Calculator에서 하위 컴포넌트 TemperatureInput 으로 정보를 전송하여 입력 후 온도 변환값이 나타나게 한다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Calculator from './chapter_12/Calculator';
ReactDOM.render(
<React.StrictMode>
<Calculator />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
터미널에서 npm start로 실행해본다.
섭씨 온도에 99를 입력하자 화씨 온도 부분에서 변환된 온도가 나타나게 된다.섭씨 110도에서 물이 끓는다.마찬가지로 화씨에서 온도를 입력하면 섭씨에서 변환된 온도값을 나타낸다.
- 리액트로 개발시, 하나의 데이터를 여러대의 컴포넌트에서 표현해야 할 때가 있음. - 이럴 경우 데이터를 각각 보관하는 게 아니라, 가장 가까운 공통된 부모 컴포넌트의 state를 공유해서 사용함 - State에 있는 데이터를 여러개의 하위 컴포넌트에서 공통적으로 사용하는 경우
이렇듯 하위 컴포넌트가 공통된 부모 컴포넌트의 state를 공유하여 사용하는 것
사용법 : 썹씨온도와 화씨온도 나타내기
하위 컴포넌트에서 State 공유하기
물의 끓음 여부를 알려주는 컴포넌트부모 컴포넌트, State로 온도 값을 가지고 있고, 사용자가 온도값을 변경할 때마다 handleChange() 함수 호출됨
BoilingVerdict 컴포넌트에 celsius 라는 prop으로 전달됨
입력 컴포넌트 추출하기
온도를 입력받기 위한 TemperatureInput 컴포넌트
위의 Calculator 컴포넌트에서 온도를 입력 받는 부분을 추출하여 별도의 컴포넌트로 만들었다. 추가적으로 props로 단위를 나타내는 scale을 추가하여 온도의 단위를 썹시, 화씨로 나타내도록 함
변경한 Calculater 컴포넌트
하나는 섭씨, 하나는 화씨 온도를 입력받게 한다. 그러나 문제점은 사용자가 입력한 온도값이 TemperatureInput 컴포넌트의 State에 저장되기 때문에 썹씨온도와 화씨온도값을 따로 입력받으면 두개의 값이 다를 수 있다.
먼저 TemperatureInput 컴포넌트에서 온도값을 가져오는 부분을 다음과 같이 수정한다. 이렇게 하면 온도값을 컴포넌트 State에서가 아닌 props에서 가져오게 된다.
handleChange() 함수 역시 다음과 같이 수정, 변경된 온도값이 상위 컴포넌트로 전달된다.
최종적으로 완성된 TemperatureInput 컴포넌트, State는 제거되었고, 오로지 상위 컴포넌트에서 전달받은 값만을 사용
Calculator 컴포넌트 변경하기
temperature와 scale에 온도값과 단위값을 각각 저장
TemperatureInput 컴포넌트 사용 부분에서는 각 단위로 변환된 온도값과 단위를 props로 넣어주었고, 값이 변경되었을 때 업데이트 하기 위한 함수를 onTemperatureChange에 넣어줌. 즉 섭씨온도가 변경되면 단위가 'c'로 변경되고 화씨온도가 변경되면 단위라 'f'로 변경된다.
최종적으로 완성된 구조
상위 컴포넌트 Calculator에서 온도값과 단위값을 각각 가지고 있으면, 두개의 하위 컴포넌트는 각각 섭씨와 화씨로 변환된 온도값과 단위, 그리고 온도를 업데이트하기 위한 onTemperatureChange() 함수를 props로 가지고 있다.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import SignUp from './chapter_11/SignUp';
ReactDOM.render(
<React.StrictMode>
<SignUp />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
- 각 객체나 아이템을 구분할 수 있는 고유한 값 - Key의 값은 같은 List에 있는 Elements 사이에서만 고유한 값이면 된다.
다음과 같이 같은 Element 사이에서의 고유한 값이면 된다.
key로 값을 사용하는 경우. 이러면 중복값이 있을 경우 키값도 중복되기 때문에, 고유해야하는 key의 조건에 충족되지 않는다. (사용 x)key로 id를 사용하는 경우, id값은 고유한 값이기 때문에 key 값의 조건에 충족한다. (권장)key로 index를 사용하는 경우, index도 고유한 값이지만 아이템의 순서가 바뀔 경우 문제가 되기 때문에 권장X
여러개의 Component 렌더링 하기
- map() : 배열에 들어있는 각 변수에 처리를 한 뒤 리턴 - const doubled = numbers.map((number) => number * 2); - 위는 numbers 배열의 각 변수를 2배로 한 뒤 리턴
numbers 배열에 잇는 값들로 map() 함수를 이용해 listItems 라는 컴포넌트 배열을 만든다. 그리고 ul 태그안에 넣고 렌더링한다.
그럼 다음과 같은 결과가 리턴될 것이다.
주의할 점!
- map() 함수 안에 있는 Elements는 꼭 key가 필요하다.
map() 함수를 이용할 때, 키값을 설정하지 않으면 다음과 같은 오류가 뜬다.
실습 : 출석부 출력하기
import React from "react";
const students = [
{
name: "Inje",
},
{
name: "Steve",
},
{
name: "Bill",
},
{
name: "Jeff",
},
];
// map() 함수 사용
function AttendanceBook(props) {
return (
<ul>
{students.map((student) => {
return <li>{student.name}</li>
})}
</ul>
);
}
export default AttendanceBook;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import AttendanceBook from './chapter_10/AttendanceBook';
ReactDOM.render(
<React.StrictMode>
<AttendanceBook />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
다음과 같이 map() 함수를 활용하여 student 배열에 있는 학생 이름들을 나열시켰다. 그런데, 문제는 map() 함수 사용 시, Key 값을 사용하지 않았기에 에러가 문구가 뜨는 것을 확인할 수 있다.
- 조건에 따른 렌더링(조건부 렌더링) - 어떠한 조건에 따라서 렌더링이 달라지는 것 - 예) Ture이면 버튼을 보여주고 False이면 버튼을 가린다.
만약 로그인됐으면 유저를 환영한다는 컴포넌트를 반환하고, 그렇지 않으면 게스트를 환영한다는 컴포넌트를 반환
Truthy, Falsy
- true는 아닌데 true로 여겨지는 값 - false는 아니지만 false로 여겨지는 값
truthy와 falsy의 예
Element Variables
- 렌더링 해야하는 컴포넌트를 변수처럼 사용해야 할 때
button이라는 변수에 컴포넌트를 집어넣어서 리턴하는 방식
Inline Condition
- 코드를 별도로 분리하지 않고, 조건문을 코드 안에 집어넣음
Inline If
- If문의 경우 && 연산자를 사용 - true && expression 첫번째 조건문이 true라서 두번째 expression 문도 평가함 - false && expression 첫번째 조건문이 false면 두번째 expression 문은 굳이 평가하지 않음 - 그래서 위의 결과값은 expression이고 아래의 결과값은 false
읽지않은 메세지 갯수가 0보다 클 경우 해당 태그를 리턴하지 않고, 0보다 작을 경우 해당 태그 리턴
Inline If-Else
- ? 연산자를 사용 - 조건에 따라 각기 다른 Element를 렌더링 하고 싶을 때 사용 - condition ? true : false (삼향연산자)
삼향연산자, 로그인되었다면 '로그인'을 반환하고 아니라면 '로그인하지 않은'을 반환이 역시, 로그인 여부에 따라 반환하는 컴포넌트를 다르게 함
Component 렌더링 막기
- null을 리턴하면 렌더링 되지 않음
경고 메세지가 없다면 null을 반환하고 경고 메세지를 나타나게 하지 않음showWarning의 값에 따라 경고 메세지를 감추거나 보이게 할 수 있음
실습 : 로그인 여부를 나타내는 툴바 만들기
import React from "react";
const styles = {
wrapper: {
padding: 16,
display: "flex",
flexDirection: "row",
borderBottom: "1px solid grey",
},
greeting: {
marginright: 8,
},
};
function Toolbar(props) {
const { isLoggedIn, onClickLogin, onClickLogout } = props;
// 로그인 여부에 따라 환영 여부를 보여주거나 감추고
// 로그인 여부에 따라 로그인, 로그아웃 버튼을 보여준다
return (
<div style={styles.wrapper}>
{isLoggedIn && <span style={styles.greeting}>환영합니다!</span>}
{isLoggedIn ? (
<button onClick={onClickLogout}>로그아웃</button>
) : (
<button onClick={onClickLogin}>로그인</button>
)}
</div>
);
}
export default Toolbar
툴바 컴포넌트를 만든다. 로그인 여부에 따라 환영 인사를 보여주고, 로그인, 로그아웃 버튼을 보여주는 방식이다.
import React, { useState } from "react";
import Toolbar from "./Toolbar";
// useState 훅을 이용해 사용자의 로그인여부를 자체적으로 관리
function LandingPage(props) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const onClickLogin = () => {
setIsLoggedIn(true);
};
const onClickLogout = () => {
setIsLoggedIn(false);
};
// Toolbar 컴포넌트에 전달
return (
<div>
<Toolbar
isLoggedIn={isLoggedIn}
onClickLogin={onClickLogin}
onClickLogout={onClickLogout}
/>
<div style={{ padding: 16 }}>리액트 공부 같이해요!</div>
</div>
);
}
export default LandingPage;
useState 훅을 이용해 사용자의 로그인 여부를 저장한다. 역시 처음에는 false가 초기값이다. 누르면 로그인 여부를 바꿔주는 onClickLogin과 onClickLogout을 Toolbar 컴포넌트에 전달하고, 로그인 여부가 저장되어 있는 isLoggedIn 역시 전달한다. 전달 받은 값들을 위의 Toolbar 컴포넌트에서 활용하면 된다.
로그인 버튼을 누르면 환영 메세지가 뜨고, 로그아웃 버튼을 반환한다. 로그아웃 버튼을 누르면 원래대로 돌아가게 된다.