1. Props

  • props : properties(속성)
  • 단어 뜻 그대로 컴포넌트의 속성값입니다.
  • props는 부모 컴포넌트로부터 전달 받은 데이터를 지니고 있는 객체입니다.
  • props를 통해 부모 컴포넌트로부터 자식 컴포넌트에게 state의 상태값, event handler를 넘겨줄 수 있습니다.

 

html에서 input태그를 쓸 떄 type="password"이런식으로 여러 속성을 주면 같은 태그인데 다르게 동작시킬 수 있었다.

그런 것처럼 컴포넌트도 이렇게 type이렇게 속성을 받을 수 있는데 input태그는 정해진 속성만 받을 수 있는 반면에 

컴포넌트는 우리가 만드는 것이니까 무슨 속성을 받아서 어떻게 쓸 지 정할 수 있다.  

 

그래서 속성들을 받아서 어딘가에 저장을 해야한다.  그래서 속성들을 저장해 놓는걸 객체 형태로 저장해 놓는다.

그래서 컴포넌트들은 props라는 객체를 기본적으로 내장하고 있다.

 

<디렉토리 구조>

 

<Child.js>

import React from "react";

class child extends React.Component {
  render(){
    return <h1>안녕하세요</h1>;
  }
}

export default Child;

 

 

<Parent.js>

 

import React from "react";
import Child from "./Child"

class child extends React.Component {
  render(){
    return <Child />
  }
}

export default Parent;

 

 

 

이런식으로하면 자식을 계속 재활용할 수 없다 무조건 안녕하세요라는 말밖에 못한다.

 

이럴 때 props를 쓴다. 

 

모든 컴포넌트는 props라는 객체를 내장하고 있다.

console.log로 확인해보자

 

<Child.js>

import React from "react";

class child extends React.Component {
  render(){
    console.log("Props>>>>>",this.props) //추가
    return <h1>안녕하세요</h1>;
  }
}

export default Child;

 

props는 컴포넌트가 가지고 있는거니까 앞에 this를 써준다. 

이 this가 엄밀히 따지고 들면 오브젝트로 props를 호출하는 대상 이런건데  

지금은 그냥 만들어진 컴포넌트를 가르키는 것이다 라고 생각하면 된다.

 

명확히하면 클래스는 아니고 클래스는 그 자체로는 아무것도 못만들잖아요.

그래서 클래스로 오브젝트를 만들잖아요 그 만들어진 오브젝트들이 다 각각의 this가 되는것이다.

나는 나의 props를 보고 싶은거니까 this(나).props로 보는 것

 

 

 

지금은 부모로부터 아무런 속성도 주지않았으니까 아무것도 안들어가 있다.

 

그러면 props를 자식한테 주입시켜보자.

 

Parent.js

import React from "react";
import Child from "./Child"

class child extends React.Component {
  render(){
    return <Child message="22기 여러분 안녕하세요" />
  }
}

export default Parent;

 

message(속성이름) "22기 여러분 안녕하세요"(속성값)

 

 

<Child.js>

import React from "react";

class child extends React.Component {
  render(){
    return <h1>{this.props.message}</h1>;
  }
}

export default Child;

 

this.props.message자체는 string이지만 객체의 값이다. 그러니 자바스크립트니까 jsx에서 써주기 위해 {}안에 넣어준다.

 

 

이렇게 하면 근본적인 큰 틀의 형태는 갖고 안의 내용들만 props를 통해서 제어할 수 있게 되는 것이다.

 

props로 넘길 수 있는건 어떤 값이던 제한이 없다. 

자바스크립트 데이터타입 스트링 넘버 어레이 오브객체 불린 다넘길 수 있다. 심지어 함수도 가능하다.

함수로 넘기는 경우를 보자

 

Parent.js

import React from "react";
import Child from "./Child"

class child extends React.Component {
  render(){
    return <Child message={22} introduce={() => alert("안녕하세요")} />
  }
}

//숫자와 함수를 props에 줬다. 문자는 {}안에 쓰지않고 그냥 ""로 주면 된다.

export default Parent;

 

함수는 자바스크립트니까 {}안에 작성한다.

 

Child.js에서 console.log(this.props)를 해보니 둘 다 잘 넘어왔다

 

Child.js에서 넘어온 introduce함수를 사용해보자

 

Child.js

import React from "react";

class child extends React.Component {
  render(){
    return <h1 onClick={this.props.introduce}>{this.props.message}</h1>;
  }
}

export default Child;

 

 

h1태그에 onClick이벤트를 걸었다.

 

 

22를 클릭하면 경고창이 나온다.

 

 

 

 

 

1. State

  • state : 상태
  • 단어 뜻 그대로 컴포넌트 내부에서 가지고 있는 컴포넌트의 상태값입니다.
  • state는 화면에 보여줄 컴포넌트의 UI 정보(상태)를 지니고 있는 객체입니다.
  • state는 컴포넌트 내에서 정의하고 사용하며 얼마든지 데이터(객체)가 변경될 수 있습니다.

state도 props와 마찬가지로 객체이다.

 

props는 주입받은 속성들을 가지고 있는 객체이다. 부모에서 전달해 주입해주는 것 

state는 상태를 가지고 있는 객체이다. 내가 가지고 있는 내 상태. 변할 수 있다! 변하지 않는 것들은 상태로 관리할 필요가 없다.

 

컴포넌트가 상태를 가져야하는 근본적인 이유는 우리가 컴포넌트 ui를 그릴 때 사용하기 위해서, ui를 그리는데 필요한 

상태들만 state에 담아놓고 관리하는 것이다.

 

그러니까 state는 컴포넌트가 가지고 있는 것이고 컴포넌트 내부에서 정의하고 사용하는 것이다.

 

 

 

 

 

Child.js

import React from "react";

class child extends React.Component {
  constructor(){
    super()
    this.state = {};
  }
  
  render(){
    return <h1 onClick={this.props.introduce}>{this.props.message}</h1>;
  }
}

export default Child;

state는 자기가 가지고 있는 거니까 내가 만들어 줘야 한다.

constructor라는 함수가 있는데 class면 내장되있는 함수다. 

class가 생성될 때 하는건데 잘모르겠이면 그냥 이 형태를 외워버리면 된다. (나중에 공부하자)

 

state는 컴포넌트가 가지고 있는 것이니까 this(나).state로 적는다. 그리고 객체니까 = {}로 만든다. 

 

class child extends React.Component {
  constructor(props){
    super(props)
    this.state = {};
  }

state에서 props값을 사용하고 싶으면 인자로 props를 넣어주면 된다.

 

 

console.log(this.state)로 콘솔을 찍어보자

지금은 state를 만들기만하고 아무것도 안넣었기 떄문에 빈객체로 나온다.

 

 

<Child.js>

import React from "react";

class child extends React.Component {
  constructor(){
    super()
    this.state = {
      titleColor:"red",
    };
  }
  
  render(){
    return <h1 onClick={this.props.introduce}>{this.props.message}</h1>;
  }
}

export default Child;

this.state안에 상태를 만들어 둘 수가 있다.

위처럼 객체 프로퍼티 만들듯이 넣으면 된다.

 

 

 

import React from "react";

class child extends React.Component {
  constructor(){
    super()
    this.state = {
      titleColor:"red",
    };
  }
  
  render(){
    return <h1 style={{color:this.state.titleColor}} onClick={this.props.introduce}>{this.props.message}</h1>;
  }
}

export default Child;

 이제 이것을 통해서 ui를 만들어야 한다, 랜더 안에서 ui를 정의한다.

인라인 스타일은 객체를 넘겨야 한다. 

 

*인라인 스타일이 속성 적용순위가 제일 높다.

 

 

근데 이걸 바꿔야 의미가 있다. 

계속 red일거면은 애초에 상태로 관리할 필요가 없다.

 

그냥 color: "red"로 집어넣으면 되지 그래서 이제 state를 바꾸는걸 해볼것이다.

 

import React from "react";

class child extends React.Component {
  constructor(){
    super()
    this.state = {
      titleColor:"red",
    };
  }
  
  changeTitleColor = () => {
    this.state.titleColor = "blue";
  }
  
  
  
  render(){
    return 
      <>
      <h1 
        style={{color:this.state.titleColor}} 
        onClick={this.props.introduce}
        >
          {this.props.message}
          </h1>;
          <button onClick={this.changeTitleColor}>타이틀 색깔</button>
      </>
  }
}

export default Child;

 

함수는 this가 바뀔 수도 있으니 애로우 펑션으로 해준다.

이렇게 했을 때 버튼을 누르면 22의 색이 파란색으로 변해야할거같은데 변하지 않는다.

 

왜냐하면 this.state.titleColor를 하면 바뀌기는 하는데 ui는 업데이트가 되지 않기 때문이다.

 

리액트는 ui를 자동으로 업데이트를 해준다 근데 얘가 매번 지가 알아서 업데이트를 할 순 없다.

업데이트가 필요해 라는 시그널을 줘야지 리액트가 알아서 ui를 업데이트 해준다.

 

그래서 ui를 업데이트해야한다는 시그널을 줄 떄는 리액트에서 제공한 함수를 써서 해줘야한다.

ui가 업데이트 되는 경우는 사실상 스테이트가 변경되었을 때 업데이트가 되면 된다.

 

이 함수는 React.Component에 내장된거기 떄문에 따로 선언하고 그냥사용할 수 있다.

 

  changeTitleColor = () => {
    this.setState({
      titleColor: "blue",
    })
  }

setSatae의 인자는 객체가 들어간다. 내가 바꾸고 싶은 객체를 넣으면 된다.

 

이제 버튼을 눌렀을 때 스테이트가 바뀌면서 리액트가 ui를 업데이트해야하는구나  인식을 하고 업데이트를 해주는 것이다.

이제 이런식으로 ui를 계속 변경하면 된다.

 

여기서 중요한거는 지금 버튼이랑 h1이 서로 연관이 있나?

서로한테는 관심이 없다. 원래 바닐라 자바스크립트라면 버튼에서 h1을 가져와서 컬러를 바꾸고 그런식으로 동작을 했다.

그런데 이제는 그렇게 절차적으로 흘러가지 않는 것이다.

모든 것은 다 스테이트만 바라보고 있는 것이다.

그래서 서로에게는 관심없다 모든건 다 스테이트 기준으로 내가 뭘하는지이다.

 

<h1>은 버튼이 있는지 없는지도 모른다. 그냥 내 컬러는 state.titleColor구나라고 밖에 모르는 것이다.

<button>은 <h1>이 있는지 없는지도 모른다. 그냥 나는 스테이트를 바꾸면 되는구나 인것이다.

각자가 따로 있는데 알아서 각자 스테이트를 통해서 자기 ui를 반영하는 그런 형태이다.

 

이제 우리는 선언하는 것이다. 보여줘야 할 ui는 이렇게 생겼어하고 끝인 것이다.

그리고 보여줘야할 ui를 결정하는 것은 이제 스테이트를 통해서 만들어 놓는거다.

우린 어떻게 보일지 선언만 해두면 알아서 업데이트 하는 완전한 패러다임의 변화다.

 

이게 바닐라 js와 리액트의 가장 큰 차이점이다.

 

*인라인 스타일링은 구리다. 쓰면 안된다. 우선 순위가 제일 높게 먹는다. 그래서 유지보수하기가 굉장히 힘들다.

만약에 내가 css에서 컬러 바꿔야지 하고 yellow로 넣었는데 안바뀌면 무슨일이지? 하고 들어가보면 인라인 스타일이

들어가 있는거다. 그럼 우선순위가 css파일의 속성을 다 무시하고 인라인 스타일이 제일 먼저니까 유지보수하기 힘들다.

 

스타일에 관련된 것은 모두 css파일에 있을거라고 생각한다. 근데  css파일을 쭉 보고 있는데 갑자기 어떤 스타일이

인라인으로 들어가서 js에도 들어가야 한다. 그러면 내가 스타일을 수정할 때 css도 보고 js도 봐야한다. 

 

이런 문제점 때문에 모든건 다 css파일로 스타일을 건드려야한다.

 

<Child.css>

.title {
  color: red;
  }
  
.title-blue{
  color: blue;
}

 

<Child.js>

import React from "react";
import "./Child.css"  //css파일 import 함

class child extends React.Component {
  constructor(){
    super()
    this.state = {
      titleColor:"red",
    };
  }
  
  changeTitleColor = () => {
    this.state.titleColor = "blue";
  }
  
  
  
  render(){
    return 
      <>
        <h1  // 인라인 스타일을 지우고 className을 줬다.
          className={this.state.titleColor === "red" ? "title" : "title-blue"} 
          onClick={this.props.introduce}
          >
            {this.props.message}
        </h1>;
            <button onClick={this.changeTitleColor}>타이틀 색깔</button>
      </>
  }
}

export default Child;

 

jsx에서 if문 못쓴다. 정확히 말하면  if문은 쓸려면 쓸 수는 있는데 그렇게 까지 쓸 이유가 없다. 

그 로직이 굉장히 힘들고 너무 길어서 복잡해진다.

 

jsx밖에서는 쓸 수 있다. 

 

대신에 삼항연산자를 쓰면 된다. 

const a = boolean ? 'true' : 'false' ;

console.log(a);

조건이 참이면 true가 a에 할당이 되고 거짓이면 false가 할당이 된다.

이 삼항연산자는 바로 값을 뱉어낸다.

 

if문은 값을 뱉어내지는 않는다. 뭐할 때 무슨 동작을 해라인 것이다.

 

삼항연산자는 자바스크립트니까 {}를 써준다.

 

 

 

이제 다시 돌아와서 props와 state를 같이 써보자

 

props는 무슨 값이든 넘길 수 있다. 그러면 props를 통해서 Parent가 가지고 있는 state를 자식한테 넘길 수도 있다.

 

그래서 부모에서 가진 state를 통해서 자식의 ui를 변경시킬 수도 있는 것이다.

 

<Parent.js>

import React from "react";
import Child from "./Child"

class Parent extends React.Component {
  constructor(){
    super();
    this.state = {
      titleColor: "red",
    }
  }
  render(){
    return (
      <>
        <Child titleColor={this.state.title.Color}/>
      </>
      );
  }
}

//숫자와 함수를 props에 줬다. 문자는 {}안에 쓰지않고 그냥 ""로 주면 된다.

export default Parent;

 

<Child.js>

import React from "react";
import "./Child.css" 

class child extends React.Component {

  render(){
    return (
      <h1 
        className={this.props.titleColor === "red" ? "title" : "title-blue"} 
        >
          타이틀
      </h1>;
    )
  }
}

export default Child;

 

이렇게 함으로써 

부모에서 Props를 보내서 자식의 ui를 변경시킬 수 있게 되는 것이다.

 

그런데 Child에서 state를 만들어서 하면 되지 왜 이렇게 할까?

리액트가 설계자체가 무조건 위에서 아래로 단반향으로 흐름이 간다. 그래서 자식에서 부모로 뭘 전달할 수 없다.

child에서 내 state는 이거야 하고 Parent로 전달할 수 없다.

 

왜 이렇게 쓸까? 유지보수 때문이다. 각자 자식에서 부모로 넘기고 형제에서 형제로 넘기고 이렇게 되면은 

근본으로 어디서 왔는지 프로그램이 커질수록 추정이 안된다. 그래서 진리의 원천(source of truth)이라고 리액트에서 말하는데

모든 진리의 원천은 한가지다. 그래서 부모에서 딱 하나 가지고 있는거다. 그러면 다 부모를 다라보고 있으니까 각자 가지고 있을 때보다

덜헷갈린다.

유지보수하고 파악하기 편한거다.

 

 

Child에서 title과 button을 컴포넌트로 분리해보자.

 

Button.js

import React from "react";

class Button extends React.Component {
  render(){
    return <button>버튼 색깔 바꾸기</button>      
  }
}

export default Button;

 

 

 

Title.js

import React from "react";

class Title extends React.Component {
  render(){
    return <h1>나는 제목</h1> 
  }
}

export default Title;

 

 

Child.js

 

import React from "react";
import Button from "./Button"
import Title from "./Title"
import "./Child.css";

class child extends React.Component {

  render(){
    return (
      <>
        <Title />
        <Button />
      </>
    )
  }
}

export default Child;

 

버튼을 누르면 타이틀의 색이 바뀌는 기능을 다시 넣어보자.

 

색을 바꾸는 거니까 타이틀에 state가 있으면 될까?

 

 

 

 

 

 

 

 

Title.js

import React from "react";

class Title extends React.Component {
  constructor() {
    super();
    this.state = {
      titleColor: "red",
    };
  }
  
  render(){
    return <h1 className={this.state.titleColor = "red" ? "title" : "title-blue"}>
           나는 제목
           </h1> 
  }
}

export default Title;

 

Button.js

import React from "react";

class Button extends React.Component {
  
  render(){
    return 
      <button>버튼 색깔 바꾸기</button>      
  }
}

export default Button;

 

지금은 안하고 있지만 각 컴포넌트 별로 자기 css파일은 자기가 가지고 있어야 한다.

지금 css를 child.css에 넣었다. 컴포넌트가 재사용되는 ui이다. css도 ui의 일부다.

그래서 각 컴포넌트마다 자기 관련된 css들은 자기가 관리하고 있어야 한다.

 

부모에서 자식에  있는 클래스네임 써가지고 바꾸고 하면 안된다.

 

그러니 child.css를 title.css로 바꿔주자.

 

 

<child.css -> Title.css>

.title {
  color: red;
  }
  
.title-blue{
  color: blue;
}

 

 

이제 Button컴포넌트 <button>태그에서 똑같이 onClik이벤트로 state를 변경해주면 되는데 어떻게 해야하나?

 

어떻게하면 타이틀에 있는 스테이트를 버튼에서 바꿀 수 있을까?

 

동등한 계층이라서 억지로 자식을 넣을 수도 없다.

 

단방향이라서 형제끼리는 서로의 스테이트를 바꾸지 못한다.

 

그래서 스테이트를 부모로 올려버리는 것이다!

 

단방향이니 얘네 형제들끼리는 소통이 불가능하지만 부모에서 관리하고 있으면 부모에서 전달해줄 수 있다.

 

그러니 Child라는 부모 컴포넌트로 올려준다.

 

 

<Child.js>

import React from "react";
import Button from "./Button"
import Title from "./Title"
import "./Child.css";

class child extends React.Component {
  constructor(){
    super();
    this.state = {
      titleColor: "red",
    }
  }

  changeTitleColor = () => {
    this.setState({
      titleColor: "blue",
    });
  }

  render(){
    return (
      <>
        <Title  titleColor={this.state.titleColor}/> //부모의 state를 전달
        <Button changeTitleColor={this.changeTitleColor}/>
      </>
    )
  }
}

export default Child;

 

<Title.js>

import React from "react";

class Title extends React.Component { 
  render(){
    return <h1 className={this.props.titleColor = "red" ? "title" : "title-blue"}>
           나는 제목
           </h1> 
  }
}

export default Title;

constructor를 삭제하고

this.state.titleColor를 this.props.titleColor로 바꿔주었다.

 

그다음 버튼은 타이틀의 스테이트는 못 건드리겠지만 부모인 Child에서 바꾸는걸 button으로 전달해주면 된다.

 

부모에서 스테이트를 바꾸는 함수를 만들고 그 함수를 버튼에게 props로 넘겨주면 된다.

버튼의 props로 넘겼다고 this가 버튼을 가리키게 되는건 아니다 왜냐하면 

changeTitleColor는 Child컴포넌트에 만들어서 넘긴거니까 this가 가르키는건 Child다. 그러니 child의 state를 바꿔준다.

 

<Button.js>

import React from "react";

class Button extends React.Component {
  
  render(){
    return 
      <button onClick={this.props.changeTitleColor}버튼 색깔 바꾸기</button>      
  }
}

export default Button;

이제 버튼을 누르면 색이 바뀐다.

 

내용을 정리하자면 서로 다른 형제들끼리 소통을 할 수 있게 부모로 스테이트를 끌어올려버리는 것이다.

 

 


모든걸 state로 쓰는 경우가 있는데 state는 변하는 상태만 state로 관리하는 것이다.

 

애초에 state를 관리를 하는게 ui를 변화시키기 위함이다. 그런데 안변하는걸 state로 가지고 있을 필요가 없다.

 

+html은 함수를 쓸 수 없다. 반면 jsx는 자바스크립트니까 함수를 써서 jsx를 만들 수 있다. ex)Array.map()

맵함수를 이용해서 반복되는 ui를 구성할 수 있다.

 

 

'React > Today I learned' 카테고리의 다른 글

리액트 매우 쉬운 별점기능 구현  (0) 2021.07.11
fetch함수를 이용한 로그인&회원가입  (0) 2021.07.02
Sass  (0) 2021.06.30
Router  (0) 2021.06.29
[React] 비동기, 동기  (0) 2021.06.27

+ Recent posts