1. state는 상태. 상태는 바꿀 수 있다.
(컴포넌트 자신이 가지고 있는 상태값. 스스로 바꿀 수 있다.)
<구조분해할당>
<기본예시>
const arr = [1,2,3];
const one = arr[0]
const two = arr[1]
const three = arr[2]
one => 1
two => 2
three => 3
//위의 과정을 거치기 번거롭기 때문에 아래와 같이 나타낸다.
//이걸 구조분해할당이라고 하는 것이다.
const [one, two, three, four] = arr;
one => 1
two => 2
three => 3
four => undefined
//객체도 가능하다.
const object = {a:1, b:2, c:3}
const {a, b, c } = object
console.log(a) => 1
console.log(b) => 2
console.log(c) => 3
<useState>
// 값 함수 초기값
const [state, setState] = useState(initValue);
//useState를 추상화해서 함수로 표현하면 대략적으로 다음과 같다.
function useState(initValue){
let state = initValue;
let setState = (value) => state = value;
return [state, setState];
}
const [name, setName] = useState("jaeSang")
state => 'jaeSang'
setState("choi") => state의 값 변경
<Hook: useState>
<기본예시>
parent.js
import React from "react";
import Child from "./Child";
function Parent(){
return{
<>
<Child />
</>
};
}
export default Parent;
child.js
import React, {useState} from "react";
function Child(props){
const [titleColor, setTitleColor] = useState("red");
return{
<div>
<h1
style={{
color: titleColor,
}}
>
Hello, JaeSang
</h1>
</div>
};
}
export default Child;
- inline style은 객체로 넣어줘야 한다. (첫번째 중괄호는 자바스크립트 문법을 쓴다는 것. 두번째 중괄호는 객체를 넣어주는 것이다.)
리액트의 좋은 점은 자동으로 UI 업데이트를 해준다는 점이다. 그 감지 시점은 재료(state,props)에 변동이 생겼을 때 이다.
그러나 리액트가 똑똑하지 못해서 titleColor를 직접 바꾸면 자동으로 감지하지 못한다. 그래서 setTitleColor로 바꿔줘야 '아 변동이 있구나'하고 인식한다.
<예시>
import React, {useState} from "react";
function Child(props){
let [titleColor, setTitleColor] = useState("red"); // const => let으로 바꿈
function changeColor(){
titleColor = "blue" // state를 직접 조작
}
return{
<div>
<h1
style={{
color: titleColor,
}}
>
Hello, JaeSang
</h1>
<button onClick={changeColor}>Change Color</button>
</div>
};
}
export default Child;
- 리액트가 변화를 인식을 하지 못해서 UI가 바뀌지 않는다.
- const를 let으로 바꿔준 이유는 '함수 내'에서 titleColor를 직접 값을 바꿨기 때문이다.
"React가 UI를 업데이트 할 때 하는 동작은 함수(컴포넌트)를 다시 한 번 실행시키는 것이다.(리랜더링 Reredering)"
- setState로 값을 바꿀 때 let으로 하지 않고 const로 해도 되는 이유는 UI가 업데이트 할 때 함수를 다시 호출하기 때문이다.( 함수는 호출되면 변수를 생성했다가 리턴 후 사라진다.)
- 즉, 첫번째 const titleColor = "red" 첫번째 호출 함수 안에
- 두번째 const titleColor = "blue" 두번째 호출 함수 안에
- 두 const는 완전히 다른 const이다.
- <h1>이나 <button>이나 서로 연관이 없다. 서로 뭐하고 있는지 모른다. <h1>은 자신의 색은 state에 있는 값이라는 것만 알고 있고 그 값이 어떻게 바뀌는지 모른다. <button>은 클릭하면 함수를 실행해서 titleColor의 state를 바꾼다는 것만 알고 있고 그 값이 어떻게 쓰이는지는 관심없다. 서로가 유기적으로 연결되어 있지 않고 모두 state만 바라보고 있어서 구조가 훨씬 단순해지는 것이다.
<반복학습 : 토글 예시>
child.js
import React, {useState} from "react";
function Child(props){
const [isSwitchOn, setIsSwitchOn] = useState(true);
const toggleSwitch = () => {
//방법1
//if(isSwitchOn){
// setIsSwitchOn(false);
//} else {
// setIsSwitchOn(true);
//}
//방법2 (삼항연산자)
//setIsSwitchOn(isSwitchOn ? false : true);
//방법3 (not연산자 사용)
setIsSwitchOn(!isSwitchOn)
}
return{
<div>
<h1>{isSwitchOn ? "스위치 켜짐" : "스위치 꺼짐"}</h1>
<button onClick={toggleSwitch}>Change Color</button>
</div>
};
}
export default Child;
<부모-자식 예시>
parent.js
import React, {useState} from "react";
import Child from "./Child";
function Parent(){
const [isSwitchOn, setIsSwitchOn] = useState(false);
const toggleSwitch = () => {
setIsSwitchOn(!isSwitchOn)
}
return{
<>
<Child isSwitchOn={isSwitchOn}/>
</>
};
}
export default Parent;
child.js
import React from "react";
function Child(props){
return{
<div>
<h1>{props.isSwitchOn ? "스위치 켜짐" : "스위치 꺼짐"}</h1>
<button onClick={props.toggleSwitch}>Change Color</button>
</div>
};
}
export default Child;
- 부모의 state를 이용해 자식 컴포넌트의 UI를 그리고 싶다. => 자식한테 부모의 state를 전해준다 (props)
- 자식에 있는 버튼이 클릭 됐을 때 부모의 state를 바꾸고 싶다. => 부모에서 만들어진 함수를 props로 전달해 준다.
컴포넌트들은 자신이 받고있는 props의 값이 바뀌면 자동으로 리랜더링(Rerendering)을 한다.
+ 부모 컴포넌트가 리랜더링이 일어나면 자식 컴포넌트들도 리랜더링이 일어난다. 물론, 부모든 자식이든 변한 부분만 반영하고 똑같은 부분은 그대로 그린다. 그러니, 위의 말은 어찌보면 당연한 말이다.
부모-자식과 같이 번거롭게 이렇게 하는 이유는 무엇일까?
리액트는 데이터의 흐름(data flow)이 단방향이다.
props : 부모 => 자식
자식 <=> 자식 (형제들끼리) (X) 서로 뭐하는지 모르기 때문에 부모를 통해서 해야한다.
<예시>
parent.js
import React, {useState} from "react";
import {Title, Button} from "./child";
function Parent(){
const [isSwitchOn, setIsSwitchOn] = useState(false);
const toggleSwitch = () => {
setIsSwitchOn(!isSwitchOn)
}
return{
<>
<Title isSwitchOn={isSwitchOn}/>
<Button toggleSwitch={toggleSwitch}/>
</>
};
}
export default Parent;
child.js
import React from "react";
export function Title(props){
return(
<h1>{props.isSwitchOn ? "스위치 켜짐" : "스위치 꺼짐"}</h1>
)
}
export function Button(props){
return(
<button onClick={props.toggleSwitch}>Change Color</button>
)
}
- Title이 state와 함수를 가지고 있다면 그것을 Button한테 넘길 수 없다.(데이터의 흐름이 위에서 아래로 흐르기 때문에)
- 그래서 공통된 부모인 Parent로 끌어올리는 것이다.