리액트 기초정리

sen·2023년 10월 20일
0

REACT

목록 보기
1/6
  • ⭐⭐⭐들어가기전에 읽어보기⭐⭐⭐
    • 일반적으로 바닐라 자바스크립트는 폭포 모델로, 위에서 아래로 순차적으로 실행되며, 한 번에 하나의 작업만 처리된다.

    • html 구조를 만들어 Dom요소에 접근해 Dom을 조작하는 방식이다.

    • 이런건 너무 인터렉티브하지 않기에 리액트JS가 나오게 되었다.

    • 참고로 리액트는 가상돔을 사용하고 상태(state)가 변경되었을 시 컴포넌트 상태가 업데이트 되는데 이말은 정리된거 다보고 다시보면 이해가 될거다.

      리액트가 뭔가?

    • 결론만 말하면, 바닐라 자바스크립트랑 반대로 기능을 통해 HTML을 생성하고 렌더링할 수 있는 라이브러리다

    • 즉, 자바스크립트로 html태그를 만들 수 있다는 뜻

      어떻게 자바스크립트에서 html을 만들 수 있나?

    • JSX라는 문법을 사용하여 JavaScript 코드 안에서 HTML과 유사한 태그 구문으로 UI를 작성할 수 있도록한다.(단, 바벨을 필수로 설치해야한다.)


    • 평소 npx create react app 명령어를 통해서 곧바로 리액트 초기설정을 해서 모르겠지만, 명령어 없이 라이브러리로만 추가해서 사용해 보면 왜 이 코드가 명령어를 사용했을때 있는지 알 수 있다.

    • jsx는 자바스크립트 코드안에서 html과 유사한 태그 구문을 사용할 수 있게 하는거라고 했는데 jsx없이 리액트 js만으로 UI를 만드는법과, jsx를 사용해서 UI를 만드는 법을 먼저 알아보자

1. JSX없이 ReactJS 사용하기

📌ReactJS

  • 기존의 바닐라 자바스크립트는 html로 구조를 만들고 자바스크립트로 기능을 만들었다.
  • 리액트js는 그 반대로 기능로직에서 html구조(태그)를 만들 수 있게한다.
  • 한줄로 말하면, element를 생성 하고 EventListener을 더하는 것을 도와준다.
  • ReactJS는 엔진, 인터렉티브한 UI를 만들 수 있게 한다.

📌React-dom이란?

  • React element들html로 바꾸어주는 라이브러리

📌컴포넌트란?

  • jsx를 반환하는 함수

→ 반환하는 함수가 컴포넌트인건 아님, 반환되는 Object가 컴포넌트인 개념으로 생각해야함.

→ 리액트의 구성하는 요소(UI)들 중에서 가장 작은 단위

  • jsx는 jsx는 리액트 js를 일반 태그처럼 보일 수 있도록 하는 문법(바벨 사용 필수)

📌<자바스크립트를 이용해 html을 넣는 방식(기존의 html→ JS의 반대 방식)>

  • 자바스크립트를 이용해 element를 만들고, ReactJs가 html로 변경
  • 어떻게 html body에 넣는가?
    • ReactDOM.render : render은 보여준다. 즉 사용자에게 보여준다.
  1. body안에 영역을 만든다.
  2. span태그를 만들어서 변수명에 담음.
  3. root안에 span태그를 넣겠다.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
  <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
  <script>
		const root = document.getElementById("root"); //span 태그가 들어갈 영역지정
    const spantag = React.createElement("span"); //span 엘리먼트생성
    ReactDOM.render(spantag, root);  //root안에 span태그를 집어넣겠다(랜더하겠다.)
  </script>
</html>

📌createElement??

  • DOM요소를 동적으로 생성하는 메소드
  • createElement는 여러 argument를 받을 수 있는데 첫번 째 인자는 만들어질 태그, 두 번째 인자로 Props가 포함된 Object를 작성할 수 있다.(id나 class이름을 지정하고, 이벤트를 부여)
  • 세 번째 인자로는 contents(내용)를 넣을 수 있다.
  • React.render을 통해 랜더링 한다.
const spantag = React.createElement("span", { id: "sein" ,style:{color:"red"},"hello");
ReactDom.rener(spantag,root)//루트안에 spantag를 랜더링 하겠다.

//이벤트 작성
const btn = React.createElement(
      "button",
      { id:"title",
				style:{color:"red"},
        onClick: () => console.log("i'm clicked"),
      },
      "Click me"
    );

2. JSX(컴포넌트를 만들어서 넣는 복잡한 방식, 원리 알기)

  • jsx문법으로, createElement 대신 기존 html 태그와 비슷한 형식으로 사용
  • 이벤트를 사용할때는 함수 형식으로 만들어주고, <태그의 첫글자 대문자 />를 사용
  • ➡️
//형식

const title = (
<h3 id="title" onMouseEnter={()=> console.log("mouse enter")}>
hello
</h3>
);

//기존 jsx없이 쓰는 reactJS
const btn = React.createElement(
   "button",
 {
  id: "title",
  style: { backgroundColor: "yellow", borderColor: "transparent" },
  onClick: () => console.log("i'm clicked"),
 },
 "Click me"
  );

🔽//jsx로 변환,bable 사용해야함
const Button = <button style={{ backgroundColor: "yellow", borderColor: "transparent" }}
onClick = {() => console.log("i'm clicked")}> Click me</button>

🔽//함수로써 사용하려면, 함수를 정의해주고, <첫글자 대문자 태그/>

const Title = ()=> (
<h3 id="title" onMouseEnter={()=> console.log("mouse enter")}>
hello
</h3>
);

const Container = ()=> <div><Title/> <Botton/></div>
ReactDOM.render(<Container />, root);

📌Babel을 사용해서 변환을 해줘야 하는 이유

  • 브라우저에서는 jsx문법을 이해하지 못함. 그래서 브라우저에서 실행 가능한 JavaScript 코드로 변환
  • const title=

    React.createElement("h3")
  • 이런식으로 자바스크립트로 해석할 수 있도록 도와줌
  • 바벨 CDN을 적어주고, 사용하고 있는 jsx의 script 태그 안에 type=text/babel을 적어줘야 함

3. JSX(많이쓰는 기존방식), state·useState

/*원래는 위에서 <Title/> <Button/>으로 연결시켰쥬 이런
복잡하게 기존의 함수 컴포넌트를 를 만들고 </>를 통해 랜더링해 주고가 
아닌, 바로 안에 태그를 넣어주는 방식*/

<script type="text/babel">
    const Container = () => (
      <div>
        <h3>점수:0</h3>
        <button>Click me</button>
      </div>);

    ReactDOM.render(<Container />, root);
</script>
  • 변수를 쓰기 위해서는 중괄호{}안에 넣어주면 된다.
  • </>를 통해 컴포넌트 안에는 다른 컴포넌트를 불러올 수 있다.

📌React.useState()

  • console.log를 출력해보면 [undefined, f]배열을 받을 수 있는데, 첫번째가 초기 데이터, 두번째 함수가 데이터를 바꿀 때 사용하는 함수다.
  • ()안에 함수의 초기 값을 설정 할 수 있다. 그래서 따로 초기 값 함수를 설정 안 해도 된다.
  • 정리하면, useState는 숫자형 데이터를 첫번째 인자에 건내주고, 그 데이터 값을 바꿀 수 있는 함수도 같이 주는 것이다.
  • const [state, setState] = React.useState(0);를 사용하는 이유는 아래의 코드와 같은 원리(구조분해할당)
const x =[1,2,3]
const a = x[0]; //1
const b = x[2]; //3

🔽구조분해할당
const x =[1,2,3]
const [a,b,c]= x; //a = 1, b = 2, c = 3

📌setState

  • 어떤 값을 부여하든 그 값으로 업데이트하고 리렌더링 까지 한다.
  • modifier함수로 state를 바꾸어 컴포넌트를 재 생성시킨다.
  • 즉, 데이터가 바뀔 때 마다 컴포넌트를 재 생성하고(리렌더링), UI를 refresh한다.
💡 **setState를 정의하는 방법(현재 state를 기반으로 다음 state를 계산하고 싶다면??)**
  • setState(counter+1)와 같이 정의하면 외부에 의해 값이 변할 수 있다.
  • setState((현재값)⇒ 계산식); ⇒ setState((current)⇒current+1);

📌jsx에서는 사용할 수 없는 html 태그들?

  • class라던지, label태그의 for와 같은 html을 바로 쓸 수 없다. 그래서 for라고 정의하는게 하니라 htmlFor =” ”로 정의 해줘야한다.
  • class도 className 써야함.

📌even.target.value

  • onChange를 통해 event를 사용할 수 있는데, 그 값을 event.target.value안에 서 찾을 수 있다.
  • onChange를 통해 변화를 감지하고 event를 통해 변화된 값을 확인하는 것
  • 아래는 value를 정해놓고 event를 통해서 값을 찾을 수 있는 예시다.
function App() {
      const [index, setIndex] = React.useState(0);
      const onSelect = (event) => {
        setIndex(event.target.value);
      };

      return (
        <div>
          <h1>super convertor</h1>
          <select value={index} onChange={onSelect}>
            <option value="0">minutes or Hours</option>
            <option value="1">km or miles</option>
          </select>
        </div>
      );
    }

1~3까지의 내용 코드로 정리 해봄

  1. <기존의 바닐라 자바스크립로 만든 클릭이벤트>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>바닐라 자바스크립트</title>
  </head>
  <body>
    <span>total:0</span>
    <button id="btn">click</button>

    <script>
      let count = 0;
      const button = document.getElementById("btn");
      const span = document.querySelector("span");
      function handleClick() {
        console.log("click perfect");
        count = count + 1;
        span.innerText = `total:${count}`;
      }
      button.addEventListener("click", handleClick);
    </script>
  </body>
</html>
  1. 리액트js만 사용해서 자바스크립트만으로 태그 만들기
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ReactJS</title>
  </head>
  <body>
    <div id="root"></div> // 최상단 루트는 지정해 줘야함.
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <!--1. react먼저 import -->
    <script>
      const root = document.getElementById("root");
      const spantag = React.createElement(
        "span",
        { id: "sexy-span", style: { color: "red" } },
        "Hello"
      );

      //react를 import 했기 때문에 create함수를 가진 React object를 사용할 수 있다.
      //안에들어갈 내용은 태그 이름과 같아야 한다.
      //ReactJs가 body안에 HTML을 생성(reactdom libray필요)
      //1.argument는 태그 이름 두번째는 프로퍼티가 들어갈 수 있다.(classname, id,이벤트)
      //세번째는 내용

      ReactDOM.render(spantag, root);
      //render react element를 가지고 html로 만들어 배치한다.(사용자에게 보여준다.)
      //결론:reactjs는 유저에게 보여질 내용을 컨트롤 할 수 있다.
    </script>
  </body>
</html>
  1. 이벤트 넣어보기
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ReactJS_addEvent</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script>
      //프로퍼티를 통해서 이벤트를 추가 할 수 있다.
      const root = document.getElementById("root");
      const h3tag = React.createElement(
        "h3",
        {
          onMouseEnter: () => console.log("mouse enter"),
        },
        "Hello"
      );
      const btn = React.createElement(
        "button",
        {
          onClick: () => console.log("i'm cliked"),
        },
        "click me"
      );
      //두개를 동시에 랜더하고싶다면
      const container = React.createElement("div", null, [h3tag, btn]);
      // ReactDOM.render(btn, root);
      ReactDOM.render(container, root);
    </script>
  </body>
</html>
  1. 위의 방법과 비교해서 jsx문법을 사용해서 이벤트 만들어보기
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <!-- 편리하기 위하여 createElement대신 jsx사용 -->
  <!-- 1.리액트 요소를 만들 수 있다. 2.자바스크립트의 확장문법 -->
  <!-- html과 비슷해서 편라한 것 대신 번역 프로그램 babel이 필요하다 -->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      //프로퍼티를 통해서 이벤트를 추가 할 수 있다.
      const root = document.getElementById("root");
      const Title = (
        <h3 id="title" onMouseEnter={() => console.log("mouse enter")}>
          hello i was h3tag
        </h3>
      );

      const Button = (
        <button
          style={{ backgroundColor: "red" }}
          onClick={() => {
            console.log("i'm cliked");
          }}
        >
          click me
        </button>
      );

      const container = React.createElement("div", null, [Title, Button]);

      ReactDOM.render(container, root);
    </script>
  </body>
</html>
  1. 컴포넌트 단위로 묶기
  • 태그를 함수로 만들고(()⇒{}) ,변수이름을<변수이름 />을 하면, 재사용 할 수 있는 함수가 만들어진다.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>without createElement</title>
  </head>

  <!-- state: 데이터가 저장되는곳: 바뀌는 값들 -->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      //프로퍼티를 통해서 이벤트를 추가 할 수 있다.
      const root = document.getElementById("root");

      //   const container = React.createElement("div", null, [Title, Button]);
      const Container = () => (
        <div>
          <h3>hello i'm title</h3>

          <button
            style={{ backgroundColor: "red" }}
            onClick={() => {
              console.log("i'm cliked");
            }}
          >
            click me
          </button>
        </div>
      );

      ReactDOM.render(<Container />, root);
    </script>
  </body>
</html>
  1. state1_재사용 함수를 사용하기 위한 과정
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>without createElement</title>
  </head>

  <!-- 컴포넌트를 분리하는 작업/컴포넌트를 다른 컴포넌트에 넣는다 -->
  <!-- create element 대신 함수를 만들고 불러오는 작업, 대문자로 시작해야함-->
  <!-- html과 비슷해서 편라한 것 대신 번역 프로그램 babel이 필요하다 -->
  <!-- 재사용 함수를 사용하기 위한 과정 without createElement -->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      //프로퍼티를 통해서 이벤트를 추가 할 수 있다.
      const root = document.getElementById("root");
      const Title = () => (
        <h3 id="title" onMouseEnter={() => console.log("mouse enter")}>
          hello i was h3tag
        </h3>
      );

      const Button = () => (
        <button
          style={{ backgroundColor: "red" }}
          onClick={() => {
            console.log("i'm cliked");
          }}
        >
          click me
        </button>
      );

      //   const container = React.createElement("div", null, [Title, Button]);
      const Container = () => (
        <div>
          <Title />
          <Button />
        </div>
      );

      ReactDOM.render(<Container />, root);
    </script>
  </body>
</html>
  1. state2
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>bad example to state</title>
  </head>
  <!--직접화면에 렌더링하기
    화면에 어떻게 업데이트 되는지 원리를 파악하기 위한 정도 :render함수를 추가한다 -->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const root = document.getElementById("root");
      let counter = 0;
      //이전 js에서는 counter을 넣기위해 innertext를 사용했다. span.innerText = `total:${count}`;
      //리액트에서는 중괄호를 열어주고 변수 이름을 담으면 된다.

      function countUp() {
        counter = counter + 1;
        render();
      }

      function render() {
        ReactDOM.render(<Container />, root);
      }
      // 이떄까지는 랜더링한 부분만 있는 거지 UI에서 볼 수 있도록 리랜더링 하는게 없었다
      //  한마디로 countUP함수를 부를 때마다 render될 수 있도록..!

      //   const container = React.createElement("div", null, [Title, Button]);
      const Container = () => (
        <div>
         
        <h3>total click :{counter}</h3> 
          <button onClick={countUp}>click me</button>
        </div>
      );

      // ReactDOM.render(<Container />, root);
      render();
    </script>
  </body>
</html>

<!-- <변경된 부분만 업데이트 되는게 좋은이유>
1. Vanilla JavaScript
Vanilla JavaScript에서는 DOM 변경을 직접 처리합니다., 필요한 DOM 요소를 직접 선택하고, 요소의 속성을 변경하거나 새로운 요소를 추가하거나 기존 요소를 제거하는 등의 작업을 직접 수행합니다.
DOM 변경이 발생하면, 브라우저는 변경된 DOM 트리를 다시 계산하고, 렌더 트리를 다시 생성한 후 화면에 그립니다. 이 과정은 비용이 많이 드는 연산으로, 자주 발생하게 되면 성능을 저하시킬 수 있습니다.

2. ReactJS
ReactJS는 DOM 변경을 처리하기 위해 가상 DOM(Virtual DOM)이라는 개념을 도입합니다. ReactJS에서는 먼저 메모리에 가상 DOM 트리를 생성합니다. 이 트리는 실제 DOM 트리의 사본으로서, 실제 DOM 트리와 별도로 존재합니다.
ReactJS는 상태 변경이 발생할 때마다 새로운 가상 DOM 트리를 생성하고, 이전의 가상 DOM 트리와 비교하여 변경된 부분을 파악합니다. 이렇게 파악된 변경 사항만 실제 DOM에 반영하는 방식을 사용합니다. 이 과정을 '재조정(Reconciliation)' 또는 'Diffing'이라고 부릅니다.
가상 DOM을 사용함으로써, 변경이 필요한 최소한의 요소만 실제 DOM에 반영되기 때문에 불필요한 연산을 줄이고 성능을 향상시킬 수 있습니다.

따라서, ReactJS는 복잡한 UI 업데이트를 효과적으로 처리할 수 있으며, 이를 통해 웹의 응답성을 향상시키고 사용자 경험을 개선할 수 있습니다
추가로 일반 자바스크립트를 쓴 브라우저는 노드정보가 바뀔때마다 노드트리를 처음부터 다시 생성한다 5단계에 걸쳐서. 근데 리액트는 가상돔을 써서 우리 시야에 보이는 부분만 수정해서 보여주고 모든 업뎃이 끝나면 일괄로 합쳐서 실제 돔에 던져준다 -->
  1. state3
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>good example to state</title>
  </head>

  <!-- reactjs 내에서 데이터를 보관하고 자동으로 리렌더링 일으킬 수 있는 방법-->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const root = document.getElementById("root");

      //React.useState(0); 가 이전에 했던 countUp함수를 대신 해주는것(초기설정과 바뀌는 함수)
      function App() {
        const data = React.useState(0); //초기설정 가능

        console.log(data); //[undefined ,f] undefined:data, f:data를 바꿀 함수
        return (
          <div>
            <h3>total click :0</h3>
            <button>click me</button>
          </div>
        );
      }

      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>

<!-- 
 구조 분해 할당 다음장 state04
 -->
  1. state4
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>good example to state2_구조분해 할당</title>
  </head>

  <!-- 구조분해 할당으로 데이터를 가져오기 const [counter, modifier] = React.useState(0);-->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const root = document.getElementById("root");

      //React.useState(0); 가 이전에 했던 countUp함수를 대신 해주는것(초기설정과 바뀌는 함수)
      function App() {
        // const data = React.useState(); //초기설정 가능

        const [counter, modifier] = React.useState(0);
      
        // console.log(data); //[undefined ,f] undefined:data, f:data를 바꿀 함수
        return (
          <div>
            <h3>total click :{counter}</h3>
            <button>click me</button>
          </div>
        );
      }

      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>

<!-- 
  const food = ["tomato","potato"] 이라는 배열이 있다 꺼내는방식2가지

  원래는 
  const favtomato = food[0];이렇게 해야되는데
  
  

  구조 분해 할당으로
   const [myFavFood,mySecondFavFood] = food;
    my FavFood //tomato

  

    구조분해 할당이란?
    열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식
 -->
  1. state5
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>why we use modifier</title>
  </head>

  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const root = document.getElementById("root");

      //React.useState(0); 가 이전에 했던 countUp함수를 대신 해주는것(초기설정과 바뀌는 함수)
      function App() {
        const [counter, setCounter] = React.useState(0);
        const onClick = () => {
          // counter = counter + 1;
          // console.log(counter);

          setCounter(counter + 1);
        };

        return (
          <div>
            <h3>total click :{counter}</h3>
            <button onClick={onClick}>click me</button>
          </div>
        );
      }

      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>
<!-- 새로 랜더 된걸로 다시 랜더 -->
  1. state6
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>state를 설정하는 방법</title>
  </head>
  <!-- 현재값에서 바뀐 state값을 설정하는 방법
  1. setCounter를 이용해서 바로 바꾸는 방법 / setCounter(12345);
  2. 이전값을 이용해서 현재값을 계산하는 방법  setCounter((current)=>current+1);
-->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const root = document.getElementById("root");

      //사용자들이 input을 어떻게 얻는지와 form을 만들었을때 state는 어떤식으로 작용하는지
      function App() {
        const [counter, setCounter] = React.useState(0);
        const onClick = () => {
          //setCounter(counter+1);//확실히 현재값인지 모른다 어디서 바뀔지 몰라서
          setCounter((current) => current + 1);
        };

        return (
          <div>
            <h3>total click :{counter}</h3>
            <button onClick={onClick}>click me</button>
          </div>
        );
      }

      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>

<!-- 
 current로 해야 안전한 이유
setState() 즉각적인 명령이 아니라 컴포넌트를 갱신하라고 요청.
setState()는 컴포넌트를 항상 즉각적으로 갱신하지 않음. 오히려 여러 변경 사항과 함께 일괄적으로 갱신하거나, 나중으로 미룰 수 있음. 이로 인하여 setState()를 호출하자마자 this.state에 접근하는 것은 잠재적인 문제가 될 수 있음.
이전 state값을 기준으로 state값을 설정해야 한다면 setState((state)=> state+1)을 사용하는 것이 안전함
(공식문서참고, https://ko.reactjs.org/docs/react-component.html#setstate)

 -->

state까지 실습_ 단위변환기 만들기

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>unit convert2</title>
  </head>
  <!-- 실제로 변환기 만들기 -->
  <!-- 단위 변환 앱 => 시간 , 시간 =>-->
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      function App() {
        const [amount, setAmount] = React.useState(0);
        const [flipped, setFlipped] = React.useState(false);
        const onChange = (event) => {
          setAmount(event.target.value);
        };
        const reset = () => setAmount(0);
        // const onFlip = () => setFlipped(!flipped);
        const onFlip = () => {
          reset();
          setFlipped((current) => !current);
        };

        return (
          <div>
            <h1>super Converter</h1>
            <div>
              <label htmlFor="minutes">Minutes</label>
              <input
                value={flipped ? amount * 60 : amount}
                type="number"
                placeholder="Minutes"
                id="minutes"
                onChange={onChange}
                disabled={flipped}
              />
            </div>
            <div>
              <label htmlFor="hours">Hours</label>
              <input
                value={flipped ? amount : Math.round(amount / 60)}
                type="number"
                placeholder="hours"
                id="hours"
                disabled={!flipped}
                onChange={onChange}
              />

              <button onClick={reset}>Reset</button>
              <button onClick={onFlip}>Flip</button>
            </div>
          </div>
        );
      }
      const root = document.getElementById("root");
      ReactDOM.render(<App />, root);
    </script>
  </body>
</html>

4. Props

Properties 의 약어

  • 부모 컴포넌트로부터 자식 컴포넌트에 데이터를 보낼 수 있게 하는 방법
  • 자식의 인자에 부모 컴포넌트의 데이터를 전달한다.
  • 자식은 부모의 데이터를 {props. 부모가 지정한 key}를 통해 사용할 수 있다.
  • 또한, 바로 사용하는 방법(프로퍼티를 오브젝트에서 꺼내는 방법)으로 인자에 props대신 바로 {부모가 지정한 key}를 적어줘도 된다.
function btn(props){
return(
	<div>
		{props.sein}
	</div>
)
}; //자식

function app(){
	return(
		<div>
			<btn sein= "this into props"/> 
			//btn({sein:"this into props"})이렇게 들어간다 생각하면 됨
		</div>
		)//부모
};

---------------------바로 사용하는 방법🔽------------------

function btn({sein}){
return(
	<div>
		{sein}
	</div>
)
}; //자식

function app(){
	return(
		<div>
			<btn sein= "this into props"/> 
			//btn({sein:"this into props"})이렇게 들어간다 생각하면 됨
		</div>
		)//부모
};
💡 **알아 둘것**
  • 컴포넌트 안에 작성 한건 전부 props다
  • onClick을 return안이 아니라 컴포넌트 안에 넣는다면 이벤트리스너가 아니라 props로 취급된다.
  • 같은 원리로 컴포넌트 안에서 style을 바로 줄 수없다.→ object형태로 줘야함
  • 컴포넌트는 자동으로 리턴문에 들어가서 실행 되어지는게 아니라, 자식 컴포넌트에서 props를 지정해줘 내가 직접 실행을 시켜줘야 한다. ⇒ 다시 새로 써와.

📌React Memo(불필요한 렌더링 막기)

  • props가 변경되지 않는 한에서 렌더가 되지 않도록 막을 수 있다

  • const 변수 =React.memo();

  • 컴포넌트가 React.memo()로 wrapping 될 때, React는 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing)한다. 그리고 다음 렌더링이 일어날 때 props가 같다면, React는 메모이징(Memoizing)된 내용을 재사용한다.

    📌PropTypes 모듈

  • npm i prop-types ⇒ import PropTypes from ‘prop-types’;

  • 리액트는 파라미터를 잘 못 넘겨도 확인할 수 없는 문제점이 존재한다.

  • 이런 문제를 줄이기 위해서 PropTypes라는 모듈의 도움을 받을 수 있다.

  • type과 다르게 입력 되었을 경우 warning을 뜨게 할수 있고, parameter 에 값을 넣지 않는 경우 경고 메시지를 띄울수 있다.

import PropTypes from "prop-types";

function Button({ text }) {
  return <button>{text}</button>;
}

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

📌 CSS도 모듈화를 할 수 있다.

  • 모든 css를 import 하지않고 class를 이용해 원하는 부분에만 style적용
  • 여러 컴포넌트에 동일하게 style을 적용할 수 있게 된다. 같은 클래스 이름을 사용해도 문제없는 이유는 리액트에서는 랜덤으로 class이름을 부여해준다

Untitled

//css
.btn {
  color: white;
  background-color: tomato;
}

//Button.js 컴포넌트
import PropTypes from "prop-types";
import styles from "./Button.module.css";

function Button({ text }) {
  return <button className={styles.btn}>{text}</button>;
}

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

5. useEffect

  • state가 변할때마다 모든 component는 다시 렌더링 하는데 그걸 한번만 실행 할 수 있도록 한다
  • 예를들어 API를 통해 데이터를 가져올 때 컴포넌트 렌더에서 API를 부르고이후 상태가 변화할 때 그 API에서 데이터를 다시 가져오지 않게 만들 수 있다.
  • useEffect는 두개의 인자를 받는 함수이다.
  • 첫번째는 실행하고 싶은 코드, 두번째는 언제 코드가 실행 될지(디펜던시) 정해주는 것이다.
  • 디펜던시는 여러개를 받을 수 있는데, 여러개의 값 중 하나의 값이 변경 될 때 랜더링한다.
  • 한번만 실행시켜 주고 싶을때는 두번째 인자에 빈 배열을 넣으면 된당.
import { useState, useEffect } from "react";

function App() {
  const [counter, setValue] = useState(0);
  const onClick = () => setValue((prev) => prev + 1);
 
  console.log("i run all the time");//state될때 마다 실행

  useEffect(() => {
    console.log("CALL THE API....");// 첫 렌더 시점 한번만 실행
  }, []);
  return (
    <div>
      <h1>{counter}</h1>

----- keyword가 변화할때만 출력되는 코드(특정 시점에서만 렌더) -------

function App() {
  const [counter, setValue] = useState(0);
  const [keyword, setKeyword] = useState("");
  const onClick = () => setValue((prev) => prev + 1);
  console.log("i run all the time");
  const onChange = (event) => setKeyword(event.target.value);

  useEffect(() => {
    console.log("I run only once.");
  }, []);

  useEffect(() => {
		// 이렇게 디펜던시가 들어가 있으면 초기 값을 잘 봐야지 에러를 잡을 수 있다.
		// 은근히 꽤나 자주 겪는 에러임 개 빡침
    console.log("I run when 'keyword' changes.");
  }, [keyword,a,b]); //keyword가 실행될때만 코드가 실행됨

📌Cleanup function (함수형 라이프 사이클에도 간단한 설명이 되어있음 참고)

  • 효율적인 런타임 메모리 관리를 위해 쓰는 것.
  • 언제 실행 될지를 정하는 effect는 위에서 알아봤으니 없어질때 실행되는 코드를 알아보자
  • component가 destory 될때(없어질 때) 실행되는 코드
  • return문으로 함수를 만들어주면 된다.
function Hello() {
  useEffect(function () {

    console.log("hi :)");//실행될때
    return function () {
      console.log("bye :(");}; //실행되고 끝 (return문)
  }, []);

📌map()

  • 배열을 가지고 있을 때 각각의 element들을 바꿀 수 있게 해준다. ()에 함수를 넣을 수 있는데 배열의 모든 아이템에 대해 실행된다.
  • 즉, 배열에 6개의 아이템이 있다면 함수가 6번 실행된다.
  • 그리고 그 함수로부터 내가 return한 값은 새로운 배열에 들어가게 된다.다만 기존의 배열에 접근할 수 없게 된다.
[‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’].map(() => “세인”)
//[‘세인’, ‘세인’, ‘세인’, ‘세인’, ‘세인’ ‘세인’] 인 새 배열을 만들어줌
  • map은 함수의 첫 번째 인자로 현재의 item을 가지고 올 수 있다.
  • map(item) -> item이나 원하는 어떤 변수명을 넣으면 item자체를 리턴하는 것도 가능하다.
['sein','hi','hello'].map((item) => item.toUpperCase())
//['SEIN','HI','HELLO']로 하면 item이 대문자로 바뀐 새로운 배열은 만들어줌

📌fetch() 와 async await

  • fetch를 활용해서 api를 가져와 화면에 띄운다 하지만 이건 옛날방식
useEffect(() => {
    fetch(
      `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year`
    )
      .then((response) => response.json())
      .then((json) => {
        setMovies(json.data.movies);
        setLoading(false);
      });
  }, []);

------
function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);//빈 배열

const getMovies= async()=>{

    const response = await fetch(
      `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year`
    );
    const json = await response.json();
    setMovies(json.data.movies);//데이터를 받아와서 빈배열에 넣어준다
    setLoading(false);//로딩 끊어주기
  }

  useEffect(() => {
    getMovies()
  }, []);

📌컴포넌트를 분리하고 props로 값 전달하기


//movie.js
function Movie({ coverImg, title, summary, genres }) {//props로 값 전달
  return (
    <div>
      <img src={coverImg} alt={title} />
      <h2>{title}</h2>
      <p>{summary}</p>
      <ul>
        {genres.map((g) => (
          <li key={g}>{g}</li>
        ))}
      </ul>
    </div>
  );
}

export default Movie;

//app.js

import { useState, useEffect } from "react";
import Movie from "./Movie";//import 해주기

  return (
    <div>
      {loading ? (
        <h1>loading....</h1>
      ) : (
        <div>
          {movies.map((movie) => (// props에서 값을 받아 화면에 출력
            <Movie
              key={movie.id}
              coverImg={movie.medium_cover_image}
              title={movie.title}
              summary={movie.summary}
              genres={movie.genres}
            />
          ))}
        </div>
      )}
    </div>
  );
}
export default App;

6. ReactRouter

  • 사용자가 입력한 주소를 감지하는 역할을 하며, 여러 환경에서 동작할 수 있도록 여러 종류의 라우터 컴포넌트를 제공한다.
  • 한번에 렌더하지 않고 정해진 환경에 정해진 컴포넌트만 렌더하기 위함이다.
  • 이중 가장 많이 사용하는 라우터 컴포넌트는 BrowserRouter와 HashRouter이다.

BrowserRouter과 HashRouter의 차이점?

  • BrowserRouter : HTML5를 지원하는 브라우저의 주소를 감지 한다.
  • HashRouter 해시 주소(http://sein.com/#test )를 감지 한다.
  • 그냥 # 있냐 없냐 모양의 차이

📌설치하기

  • npm i react-router-dom@5.3.0 으로 설치 → 이런거는 최신버전 확인해서 설치하는게 좋음 @x.x.x 이부분이 버전임
  • import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 로 import시켜준다.
  • Switch는 route(URL)를 찾는 역할을 한다.
<Router>
      <Switch>
        <Route path="/url/:id"> // <= 얘(:id)가 리액트 라우터의 훅 useParams에서 가져오는친구
         <컴포넌트/>
        </Route>
        <Route path="/url">
				 <컴포넌트/>
        </Route>
      </Switch>
    </Router>

📌Link 컴포넌트

  • Link는 브라우저 새로고침 없이 유저를 다른 페이지로 이동시켜주는 컴포넌트다.
  • import { Link } from "react-router-dom";
  • html의 a태그 대신 <Link to=”/”></Link> 를 사용한다.

import { Link } from "react-router-dom";

function Movie({ coverImg, title, summary, genres }) {
  return (
    <div>
      <img src={coverImg} alt={title} />
      <h2>{title}</h2>
      <h2>
        <Link to="/movie">{title}</Link>
      </h2>
      <p>{summary}</p>
      <ul>
        {genres.map((g) => (
          <li key={g}>{g}</li>
        ))}
      </ul>
    </div>
  );
}

📌useParams()

  • 리액트 라우터가 변수의 값을 넘겨준다.
  • import { useParams } from "react-router-dom";로 사용
  • url상의 파라미터를 받아오는거임 ⇒ “/test/:id” ⇒ “/test/abc” ⇒ 뭐 이런식으로 쓰는거임

8. 클래스형 컴포넌트

클래스형 컴포넌트: class로 정의하고 render() 함수에서 jsx 코드를 반환

  • 클래스형 컴포넌트에서는 state(상태)를 사용할 수 있으며 각종 라이프사이클 및 메서드를 이용하여 컴포넌트가 마운트 혹은 언마운트 될 때 추가 작업을 수행시키는 등 조작할 수 있다
import {Component} from 'react'

class App extends Component{
constroctor(props){
super(props);
this.state={
 }
}
render(){
return()
}

export default App

**컴포넌트 라이프사이클**

  • Mount : DOM이 생성된 후 웹 브라우저에 나타나는 것.
  • Update : 컴포넌트에서 props가 바뀔 때, state가 바뀔 때, 부모 컴포넌트 리렌더링 때, this.forceUpdate로 강제 렌더링할 때
  • Unmount : 마운트의 반대 과정. DOM에서 컴포넌트를 제거하는 과정

**render() 함수**

  • 준비한 UI를 랜더링한다
  • 이 메서드 내부에서 this.props, this.state에 접근 가능, 리액트 요소를 반환
  • 이 메서드 내부에서는 이벤트 설정이 아닌 곳에서 setState를 사용하면 안된다.
  • 브라우저의 DOM에 직접 접근하면 안 되고, DOM 정보를 가져오거나 state를 변화시키고 싶다면 componentDidMount 메서드에서 처리해야 한다.

props

  • {this.props.변수}로 전달한다.

state

  • 컴포넌트가 내부적으로 자신의 상태를 바꾸고, 관리하기 위해 사용한다.
  • 초기값이 필요하다. this.state={}
  • state안에 들어가는 값은 props로 전달 받은 값이다
  • state값을 변경하기 위해서는 이벤트 부분에 this.setState({})로 변경해 주면 자동으로 render가 된다.
import {Component} from 'react'

class App extends Component{
constroctor(props){
super(props);
this.state={ // 초기값
	movieList:[
		{name:'영화1',rating:"3.5"},
		{name:'영화1',rating:"3.5"},
		{name:'영화1',rating:"3.5"},
	]
 }
}
const concatedList = this.state.movieList.cocat(movieObj);
this.setState({movieList:concatedList});
render(){
return(
	<div>
		<InputComp addMovieInfo={this.addMovieInfo}/>
</div>
)
}

export default App

react의 this

  • 상태값(state)나 속성값(props), 컴포넌트에 선언한 메서드를 참조하기 위해 사용한다.

9. 클래스형 라이프사이클

ComponentWillMount 메소드

  • 컴포넌트가 생성되기 전에 처리될게 있다면, 이 메소드를 구현하는 걸 통해 컴포넌트가 생성되기 전 , render 실행전 해야할 일을 만들 수 있다.
  • render가 되고 mount가 되는 것이다.

ComponentDidMount 메소드

  • 화면에 그려지고 난 뒤 실행시킬 코드
  • componentDidMount가 실행되고 props나 state를 통해 컴포넌트의 변화가 생긴다
  • 그리고 render메소드를 호출 시키는데, 두 과정 사이에서 render메소드 호출 여부를 결정 짓는 메소드를 ‘ShouldComponentUpdate 메소드’라고 한다.
  • 이 메소드는 true 리턴 시 호출된다.

ComponentWillUpdate메소드

  • ShouldComponentUpdate 메소드가 true일 시 update가 되는 메소드

ComponentDidUpdate 메소드

  • 업데이트가 끝이 났다.
  • ComponentWillUpdate 와 ComponentDidUpdate가 계속 반복되는 형태

10. 함수형 라이프사이클

  • 클래스형의 useState는 componentDidMount(첫 렌더)와 ComponentUpdate(리로드,화면고침)와 등가한다
  • 라이프사이클에서 Dom에 나타날때 초기작업을 willMount와 DidMount까지라고 생각하면, 소멸할때는 ComponentWillUnMount 메소드라고 하는데 이걸 함수형으로 어떻게 구현하나?
  • 바로, 리턴값을 통해서 한다.(clean up)
  • 구독생성버튼 예) 구독생성버튼(등장작업) → 화면(Dom) →구독취소버튼(cleanup) →삭제(소멸)

7. github build 하는법

📌npm i gh-pages

  • 결과물을 github pages에 업로드 할 수 있게 해주는 패키지
  • gh-pages가 build 폴더를 hompage로 적은 웹사이트에 업로드 해준다.

<방법>

  • npm i gh-pages → npm run build
  • package.json에 "homepage": "https://jungsein.github.io/NC_Basic_React.git”를 추가해준다
  • 깃허브 아이디 +github.io/연결된 레포지터리
  • package.json안의 script안에 “deploy”: ”gh-pages -d build”를 적어주고, “predeploy”:”npm run build” 를 적어준다.
  • 생성된 build파일을 없애주고 npm run deploy를 실행 시킨다
  • npm run build는 react-scripts build를 실행시키고 predeploy는 npm run deploy를 실행시키는 것이다.
  • 계속 이제 수정하고 업데이트 하고 싶다면 npm run build 를 하면 자동으로 build가 된다.
profile
가보자고~!

0개의 댓글