Component 부모자식관계)

// src/App.js
import React from "react";
function Child() { // Child 라는 새로운 컴포넌트 생성
return <div>나는 자식입니다.</div>;
}
function App() { // App 컴포넌트는 Child 컴포넌트를 자식으로 삼음
return <Child />;
}
export default App;
// App.js에서 내보내진 컴포넌트는 App이므로 화면에 보여진 건 App 컴포넌트!!
JSX)
함수로 만들어진 컴포넌트를 html 태그 사용하듯이 코드를 작성하는 방식!
Component의 생김새를 정의할 때 사용하는 문법
주의할점!!
1. 태그 닫기
// src/App.js
import React from "react";
function App() {
return (
<div className="App">
<input type="text"> // <input type='text'/> 로 수정 필요
</div>
);
}
export default App;

2. 무조건 1개의 엘리먼트를 반환
return (
<p>안녕하세요! 리액트 반입니다 :)</p>
<div className="App">
<input type='text'/>
</div>
);

=> 아래처럼 하나의 엘리먼트로 수정
return (
<div className="App">
<p>안녕하세요! 리액트 반입니다 :)</p>
<input type='text'/>
</div>
);
3. JSX에서 javascript 값을 가져올 때 중괄호 쓰기
// src/App.js
import React from "react";
function App() {
const cat_name = "perl";
return <div>hello {cat_name}!</div>;
} // hello perl!
// return <div>hello cat_name!</div>; // hello cat_name!
export default App;
4. class 대신 className
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
const number = 1;
return (
<div className="App"> // class가 아니라 className
<p>안녕하세요! 리액트 반입니다 :)</p>
{/* JSX 내에서 코드 주석은 이렇게 사용 */}
<p>{number > 10 ? number+'은 10보다 크다': number+'은 10보다 작다'}</p>
// 삼항연산자!
</div>
);
}
export default App;
5. 인라인으로 style 주기
HTML :
<p style="color: orange; font-size: 20px;">orange</p>
JSX :
// 중괄호를 두 번 쓰는 이유 : 딕셔너리도 자바스크립트!!
// 이렇게 쓰거나,
<p style={{color: 'orange', fontSize: '20px'}}>orange</p>
// 타일 딕셔너리를 변수로 만들어서 사용 가능
function App() {
const styles = {
color: 'orange',
fontSize: '20px'
};
return (
<div className="App">
<p style={styles}>orange</p>
</div>
);
}
★ Props & State)
Props : 부모Component로부터 받아온 모든 데이터
*** 부모Component에서 자식Component 방향으로만 Props 전달 가능!

import React from "react";
function App() {
return <GrandFather />;
}
function GrandFather() {
return <Mother />;
}
function Mother() {
const name = "홍부인";
return <Child motherName={name} />;
}
// Mother Component가 가지고 있는 name를 자식 Component로 전달
// Child Component가 name 데이터를 알기 위해 motherName={name}
// Props는 Component 간의 정보를 교류할 때 사용됨
function Child(props) { // 'Props로 name을 전달했다'고 한다.
console.log(props);
return <div>연결 성공</div>;
}
export default App;
* 객체 안에 있는 값에 접근하기 위해서는 props.motherName 이런 식으로 접근해야 "홍부인"이라는 value를 가져올 수 있음
function Mother() {
const name = "홍부인";
return <Child motherName={name} />;
}
function Child(props) {
console.log(props);
return <div>{props.motherName}</div>;
}

예시)
// src/App.js
import React from "react";
function App() {
return <GrandFather />;
}
function GrandFather() {
const name = "HarryPotter";
return <Mother grandFatherName={name} />;
}
function Mother(props) {
console.log(props);
return <Child grandFatherName={props.grandFatherName} />;
}
function Child(abcd) {
return <div>{abcd.grandFatherName}</div>;
}
//가독성을 위해 파라미터 이름 Props로 쓰긴 했지만, 꼭 이름이 Props일 필요는 없음!
// abcd로 적어도 됨
export default App;

+) Props를 구조분해할당으로 받는 법
Props 구조는 grandFatherName : "HarryPotter"가 있는 객체로 되어 있다
아래 코드를 크롬 콘솔창에 입력해 보기!
const props={grandFatherName : "HarryPotter"}
const {grandFatherName}=props
console.log(grandFatherName) // HarryPotter

=> Child와 Child Component에 props 대신 {grandFatherName}를 넣음
// src/App.js
import React from "react";
function App() {
return <GrandFather />;
}
function GrandFather() {
const name = "HarryPotter";
return <Mother grandFatherName={name} />;
}
function Mother(props) {
console.log(props);
return <Child grandFatherName={props.grandFatherName} />;
}
function Child({ grandFatherName }) {
return <div>{grandFatherName}</div>;
}
export default App;
=> 크롬창에 HarryPotter 출력
(추가 설명은 아래 ctrl + f 로 구조분해 할당과 Props 검색 ↓)
Children)
* 부모 컴포넌트로 정보를 전달
<Component props={props} /> 와 다른 방식으로 자식 컴포넌트에 정보를 전달해주어도 사용 할 수 있는 props
import React from "react";
function User() {
return <div></div>;
}
function App() {
return <User>안녕하세요</User>;
}
// App 컴포넌트가 User 컴포넌트를 자식으로 품고 있음!
export default App;
=> yarn start
=> 크롬 브라우저 창에는 아무것도 뜨지 않음 : App 컴포넌트에서 ‘안녕하세요' 라는 정보를 보냈지만, User 컴포넌트에서는 그 정보를 받지 않았기 때문!
* 기존의 <User hello='안녕하세요'> 로 props를 보내던 방식과 달리, children props를 보내는 방식은 <User>안녕하세요</User> 로 표현한다
=> User 컴포넌트에서 props.children을 받아 그대로 렌더링하면,
import React from "react";
function User(props) {
return <div>{props.children}</div>;
}
function App() {
return <User>안녕하세요</User>;
}
export default App;
=> 크롬 창에 '안녕하세요' 문구 띄우기 성공
Children 용도)
Layout 컴포넌트를 만들 때 자주 사용
Layout이라는 Component를 만들고 그 안에 들어오는 값들은 전부 같은 스타일을 주고 싶다고 가정했을 때,
Component마다 스타일을 줄 필요 없이, Layout Component 하나만 작성해 두고 그 안에 children으로 값을 주입하는 식으로 활용 가능
Layout 컴포넌트 안에는 header 라는 컴포넌트가 있고, header 아래에 {props.children} 를 통해서 props를 받아 렌더링 한다. 즉, Layout 컴포넌트가 쓰여지는 모든 곳에서 <Layout>…</Layout> 안에 있는 정보를 받아서 가져올 수 있다.
// src/About.js
import React from "react";
import Layout from "./components/Layout";
function App() {
return (
<Layout>
<div>여긴 App의 컨텐츠가 들어갑니다.</div>
</Layout>
);
}
export default App;
이 코드를 통해 Layout에 있는 header가 보여지게 되고, “여긴 App의 컨텐츠가 들어갑니다.” 라는 문장이 Layout의 props로 전달된다. 결과적으로 header 컴포넌트를 Layout 컴포넌트에서 한 번만 작성하면 여러 페이지에서 모두 보여지게 할 수 있음!

↓ Layout 컴포넌트를 About 컴포넌트에 다시 사용할 경우 ↓
// src/About.js
import React from "react";
import Layout from "./components/Layout";
function About() {
return (
<Layout>
<div>여긴 About의 컨텐츠가 들어갑니다.</div>
</Layout>
);
}
export default About;

+ 구조분해 할당과 Props)
: 구조 분해 할당 통해 props 내부값 추출하기
지금까지 자식 컴포넌트에서 props를 받을 때 사용한 방법
todo 라는 props를 사용하는 모든 곳에서 props. 를 붙여줘야 함
function Todo(props){
return <div>{props.todo}</div>
}
props는 object literal 형태의 데이터
구조분해 할당으로 조금 더 짧게 쓸 수 있음!
function Todo({ title }){
return <div>{title}</div>
}
만약 여러개의 props를 받는다면, { } 안에 여러개의 props를 그대로 써주면 된다
function Todo({ title, body, isDone, id }){
return <div>{title}</div>
}
defaultProps)
Props를 받아오는 값에는 Default Props를 설정할 수 있음
컴포넌트를 만들고 자식 컴포넌트에서 props를 받다보면, 자주 받거나 또는 무조건 받아야 하는 props들이 있다
(값이 없으면 문제가 되거나 UX가 어색해지는 경우)(ex. 로그인이 완료되기 전까지 사용자 이름을 받아오지 않았는데 "(공백) 님 안녕하세요!")
// components/Child.js
import React from 'react';
function Child({ name }){
return <div>내 이름은 {name} 입니다. </div>
}
export default Child
이런 경우 부모 컴포넌트에서 props를 받기전까지, Child Component에서 Default Props로 값을 미리 설정해 보여줄 수 있음. 이후에 부모 컴포넌트에서 name props가 오게되면 설정된 defaultProps는 사라지고 내려 받은 props로 값이 바뀌게 된다
// components/Child.js
// default props 지정하기
import React from 'react';
function Child({ name }){
return <div>내 이름은 {name} 입니다. </div>
}
Child.defaultProps={
name: '기본 이름'
}
export default Child
import React from 'react';
// 구조 분해 할당 문법을 사용하면 이렇게도 가능!!!
function Child({ name = '기본이름' }){
return <div>내 이름은 {name} 입니다. </div>
}
export default Child
State)
: Component 내부에서 바뀔 수 있는 값
Mother Component가 name이라는 값을 가지고 있을 때,
function Mother(props) {
const name = "홍부인";
return <Child grandFatherName={props.grandFatherName} />;
}
const name = "홍부인" 이런 식으로 값을 할당했는데, 만약 value를 고정하는 것이 아니라 이름이 계속 바뀌어야 한다면(ex 로그인하는 경우) 변수 name은 State로 생성해야 한다
State를 만들 때는 useState()를 사용함
useState() : 리액트에서 제공하는 state를 만들어주는 기능(을 훅Hook이라고 부른다 - https://ko.reactjs.org/docs/hooks-intro.html)
State를 생성하는 코드
const [ value, setValue ] = useState( 초기값 )
value : State의 이름 - ex. name
setValue : set(State의 이름) - ex. setName
useState(초기값) : 원하는 초기값 입력(initialState라고 한다) - ex. useState("HarryPotter")
const [name, setName] = useState("HarryPotter");
setValue는 State를 변경할 때 사용하는 함수로, 이 함수를 통해서만 State 변경이 가능!
import React, { useState } from "react";
function Child(props) {
return (
<div>
<button
onClick={() => {
props.setName("JamesPotter"); // 받은 setName을 실행!
}}
>
JamesPotter로 이름 바꾸기
</button>
<div>{props.grandFatherName}</div>
</div>
);
}
function Mother(props) {
return (
<Child grandFatherName={props.grandFatherName} setName={props.setName} /> // 받아서 다시 주고
);
}
function GrandFather() {
const [name, setName] = useState("HarryPotter"); // state를 생성
return <Mother grandFatherName={name} setName={setName} />; // 주고
}
function App() {
return <GrandFather />;
}
export default App;
=> <JamesPotter로 이름 바꾸기> 버튼 클릭 시, HarryPotter는 JamesPotter로 변경
※주의할 점!※
JS에서 바뀔 수 있는 값은 const가 아닌 let으로 선언하면 되는데, let이 아닌 State로 선언하는 이유?
Component에는 LifeCycle이라는 개념이 있고, LifeCycle에 따라 화면 값이 변했을 때, 그 변화된 값을 화면에 다시 rerendering시켜주기 위한 조건이 있음. 그 조건 중 하나가 State가 변경되었을 때!
let으로 선언해서 그 값에 새로운 값을 할당시켜 줄 수 는 있지만 리액트에서는 변경된 값이 화면에 바로 반영되지 않는다. State가 바뀌었다고 인식하지 못하기 때문
useState + onClick Event)
: Button에서의 useState 활용
button을 생성하고, button에 연결할 onClickHandler 함수를 만든 후, onClickHandler 함수를 button의 onClick에 주입한다
import React from "react";
function App() {
function onClickHandler() {
console.log("hello, button"); // 버튼 클릭 시 크롬 콘솔 창에 hello, button 출력
}
return (
<div>
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;
리액트에서는 이런 방식으로 함수와 Component를 연결해 준다:)
State와 이벤트핸들러 연결하기↓
import React, { useState } from "react";
function App() {
const [name, setName] = useState("HarryPotter");
function onClickHandler() {
setName("JamesPotter");
}
return (
<div>
{name}
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;
=> 버튼 클릭 시, HarryPotter는 JamesPotter로 변경!
useState + onChange Event)
: Input에서의 useState 활용
Input을 넣은 값을 State로 Value 생성
import React, { useState } from "react";
const App = () => {
// component를 작성하는 방식으로 fuction 대신 화살표 함수를 사용
const [value, setValue] = useState("");
const onChangeHandler = (event) => {
const inputValue = event.target.value;
// 사용자가 입력한 input값을 추출하기 위해 event.target.value 에 접근
setValue(inputValue);
// 추출한 값을 setValue, 즉 value의 State에 연결해서 input과 state 연결
};
console.log(value) // 크롬 콘솔창에 어떻게 찍히는지 확인해보기!
return (
<div>
<input type="text" onChange={onChangeHandler} value={value} />
</div>
);
};
export default App;
사용자가 input에 어떤 값을 입력할때마다(= onChange가 될 때마다) value라는 State에 setValue해서 값을 넣어줌!
불변성)
: 메모리에 있는 값을 변경할 수 없는 것
(JS의 데이터 형태중에 원시 데이터는 불변성이 있고, 원시 데이터가 아닌 객체, 배열, 함수는 불변성 없음)
1. 변수를 저장하면, 메모리에 어떻게 저장이 되는지?
만약 우리가 let number = 1 이라고 선언을 하면, 메모리에는 1 이라는 값이 저장된다. number 라는 변수는 메모리에 있는 1을 참조한다. 그리고 let secondNumber = 1 이라고 다른 변수를 선언을 했다고 가정했을 때, 이때도 자바스크립트는 이미 메모리에 생성되어 있는 1이라는 값을 참조한다.
즉, number와 secondNumber는 변수의 이름은 다르지만, 같은 메모리의 값을 바라보고 있다. 그래서 콘솔에 number === secondNumber 를 하면 true가 보인다.
하지만 원시데이터가 아닌 값(객체, 배열, 함수)는 다름!
let obj_1 = {name: ‘kim’} 이라는 값을 선언하면 메모리에 obj_1이 저장된다. 그리고 이어서 let obb_2 = {name: ‘kim’} 이라고 같은 값을 선언하면 obj_2라는 메모리 공간에 새롭게 저장된다. 그래서 obj_1 === obj2 는 false
2. 데이터를 수정하면?
만약에 기존에 1이던 number를 number = 2 라고 새로운 값을 할당하면, 기존 메모리에 저장이 되어 있는 1이라는 값이 변하지 않고, 새로운 메모리 저장공간에 2가 생기고 number라는 값을 새로운 메모리 공간에 저장된 2를 참조한다. 원시데이터는 불변성이 있기 때문이다. 그래서 secondNumber를 콘솔에 찍으면 여전히 1이라고 콘솔에 보인다. number와 secondNumber는 각각 다른 메모리 저장공간을 참조하고 있기 때문!
obj_1.name = ‘park’ 이라고 새로운 값을 할당할 경우, 객체는 불변성이 없기 때문에 기존 메모리 저장공간에 있는 {name: ‘kim’} 이라는 값이 {name : ‘park’} 으로 바뀌어 버린다.
원시데이터는 수정을 했을 때 메모리에 저장된 값 자체는 바꿀 수 없고, 새로운 메모리 저장공간에 새로운 값을 저장함. 하지만 원시데이터가 아닌 데이터는 수정했을 때 기존에 저장되어 있던 메모리 저장공간의 값 자체를 바꿈<<그래서 불변성이 없다고 표현함!!!>>
3. 리액트에서 원시데이터가 아닌 데이터의 불변성을 지켜주는 이유?
리액트에서는 화면을 리레더링 할지 말지 결정할 때 state의 변화를 확인한다. state가 변했으면 리렌더링, state가 변하지 않았으면 리렌더링x
그때 state가 변했는지 변하지 않았는지 확인하는 방법이 state의 변화 전, 후의 메모리 주소를 비교하는 것! 그래서 만약 리액트에서 원시데이터가 아닌 데이터를 수정할 때 불변성을 지켜주지 않고, 직접 수정을 가하면 값은 바뀌지만 메모리주소는 변함이 없게 됨. 그래서 즉, 개발자가 값은 바꿨지만 리액트는 state가 변했다고 인지하지 못하게 되고, 리렌더링은 일어나지 않음.
4. 불변성 예시
배열에 setState 할 때 불변성을 지켜주기 위해 직접 수정하지 않고, 전개 연산자를 사용해서 기존의 값을 복사하고, 그 이후에 값을 수정하는 식으로 구현한다
import React, { useState } from "react";
function App() {
const [dogs, setDogs] = useState(["치와와는 참지않긔"]);
function onClickHandler() {
// spread operator(전개 연산자)를 이용해서 dogs를 복사
// 그리고 나서 항목을 추가
setDogs([...dogs, "시고르자브르종"]);
}
console.log(dogs);
return (
<div>
<button onClick={onClickHandler}>버튼</button>
</div>
);
}
export default App;
+)
Props Drilling
PropTypes 이해하기 -> https://ko.reactjs.org/docs/typechecking-with-proptypes.html#gatsby-focus-wrapper
=> 자바스크립트는 동적 타입의 언어이기 때문에, 변수를 선언할 때 바로 타입을 지정할 필요가 없고 사용하는 쪽에서 타입이 동적으로 결정된다. 그래서 Props를 넘길 때도 이 Props의 값이 어떤 타입인지 미리 지정하지 않음
그래서 Props가 반드시 문자열이라는 의도로 작성하더라도 타입이 명확히 지정되어 있지 않기 때문에, 협업시 코드를 수정하면서 숫자값이 들어온다거나 하는 문제가 충분히 발생할 수 있다
PropTypes는 이때 props의 타입을 검사할 수 있음~~~!!
'스파르타 개발일지' 카테고리의 다른 글
| 개발일지 20221210~11 todo-list 만들기 (0) | 2022.12.11 |
|---|---|
| 개발일지 20221208 리액트 입문 3 실습 (0) | 2022.12.08 |
| 개발일지 20221206 리액트 입문 강의 시작! (0) | 2022.12.06 |
| 개발일지 20221122 SPA, Firebase Setup 특강 정리 + 두 번째 프로젝트 (0) | 2022.11.22 |
| 개발일지 20221121 (0) | 2022.11.21 |