6주차에는 React가 어떤 것인지 체험해보는 시간이었다. 이 과정을 수강하기 전에 구글링만을 가지고 어거지로 만들었던 페이지들이 생각나면서 약간 부끄럽고, 다음엔 조금 더 낫도록, 또한 왜 에러가 났었는지 좀 더 잘 알 수 있지 않을까 하는 생각이 들었다.


React 맛보기

  • 장점
    Virtual DOM / JSX / Flux / Functional Programming....
    새로운 기술을 배우는 시작점으로 좋다.
    • 새로운 좋은 라이브러리가 계속 나오고 있다.
      (like, swr, framer motion ..)

DOM (Document Object Model)

문서를 논리 트리로 표현한다.

  • 컴퓨터가 이해하는 문서 구조
  • 메모리에 웹페이지 문서 구조를 표현

Vanilla JS 순수 자바 스크립트

ㄴ 특정 라이브러리나 프레임워크를 사용하지 않은 그 자체의 자바스크립트

CDN (Content Delivery Network)

웹에서 사용되는 다양한 컨텐츠(리소스)를 저장하여 제공하는 시스템 - unpkg

createElement()

React.createElement(
  type,
  [props],
  [...Children]
)

render()

ReactDOM.render(element, container[, callback])
// 주입할 element, root로 생각할 cotainer

JSX, Babel

createElement()로 매번 요소를 만드는 것 불편하다.

JSX

문자도 HTML도 아닌 JavaScript의 확장문법

const element = <h1>Hello, world!</h1>;

Babel

-> JavaScript Complier
컴파일러 : 언어 해석기, 특정 언어를 다른 프로그래밍 언어로 옮기는 프로그램

// 바벨 불러오기
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

// 스크립트 읽게 할 코드들 적기
<script type="text/babel">
  const rootElement = document.getElementById("root");
  const element = <h1 className="title">Hello, world!</h1>
</script>
  • javascript에 가깝기 때문에 모든 것을 변수화 해서 집어넣을 수 있다.
// 바벨 불러오기
<script type="text/babel">
  const rootElement = document.getElementById("root");
  
  const text = "Hello, world";
  const titleClassName = "title1234";
  const props = {className: titleClassName, childeren: text}
  const customH1 = <h1 {... props}/>;
  // 위아래는 동일하다. // sprend 연산자 ...
  // <h1 className={props.className} children={props.children} />
  const element = customH1;
  ReactDOM.render(element, rootElement);
</script>

멀티 Element

<div id="root">
  <h1>Hi</h1>
  <h3>Bye</h3>
  <h5>Children</h5>
</div>  
  • 이걸 react로 만든다면??
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");
  const element = (
    <div
      className="box"
      children={[
        React.createElement("h1", null, "Hi"),
        React.createElement("h3", null, "Bye"),
        React.createElement("h6", null, "Children")
      ]}   
    />
  );
  ReactDOM.render(element, rootElement);
</script>  
// 해석해보면 위의 html 형태는 div아래 바로 요소들이 있는데
// react표현은 chirdren안에 요소를 넣어야 하므로 
// div className="box" 를 생성해 감싸고 그 안에 요소로 넣어야 한다
// 즉, 모양이 같지 않다.
  • 그럼 동일하게 만드는 법은 없는가?
// React.Fragment를 쓰면 동일하게 표현 가능
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");
  const element = (
    <React.Fragment
      children={[
        React.createElement("h1", null, "Hi"),
        React.createElement("h3", null, "Bye"),
        React.createElement("h6", null, "Children")
      ]}   
    />
  );
  ReactDOM.render(element, rootElement);
</script>  
// React.Fragment는 type요소 생성 없이 감싸기만 해서 
// children 요소 생성 가능하다.
  • React.creatElemet()는 JSX 표현으로 바꿔도 된다 했으므로
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");
  const element = (
    <React.Fragment
      children={[<h1>Hi</h1>, <h3>Bye</h3>, <h5>Children</h5>]}   
    />
  );
  ReactDOM.render(element, rootElement);
</script>  

// 이렇게 까지 정리가 가능하다.

const element = (
  <React.Fragment>
    <h1>Hi</h1>
    <h3>Bye</h3>
    <h5>Children</h5>   
  </React.Fragment>
);    

// 거의 html 모양과 유사해졌지만, jsx 변수표현로 만든것임 확인

const element = (
  <>   
    <h1>Hi</h1>
    <h3>Bye</h3>
    <h5>Children</h5>   
  </>
); 

// 빈태그는 < React.Fragment > 와 동일하다

Element 찍어내기

// 함수로 만들어 놓고 반복시켜 요소 찍어내기 가능
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");
  const paint = (title, description) => (
    <>
      <h1>{title}</h1>
      <h3>{description}</h3>
    </>
  );
  
  const element = (
    <>
      {paint("Good","good")}
      {paint("Bad","bad")}
      {paint("SoSo","soso")}   
    </>
  );
  ReactDOM.render(element, rootElement);
</script>  

  • props 개념으로 만든 것도 이해해보자
// 함수로 만들어 놓고 반복시켜 요소 찍어내기 가능
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");
  // 이렇게 표현하고 싶으면 꼭 첫 글자를 대문자로 표현해줘야한다.
  const Paint = ({ title, description }) => {
    return (
      <>
        <h1>{title}</h1>
        <h3>{description}</h3>
      </>
    );
  };
  
  // { return } 생략 가능함 기억하자.
  
  const element = (
    <>
      <Paint title="Good" description="good" />
      <Paint title="Bad" description="bad" />  
      <Paint title="SoSo" description="soso" /> 
    </>
  );

  ReactDOM.render(element, rootElement);
</script>  

// 동일하게 작동한다.

Children 제한 없다. 갯수 무제한

JS, JSX 섞어쓰기

// 함수 매개변수로 첫글자 대문자로 넣으면 h1으로 출력,
// 소문자이면 h3로 출력되게 만들어 보겠다.

<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");

  const Text = ({ text }) => {
    if (text.charAt(0) === text.charAt(0).toUpperCase()) {
      return <h1>{text}</h1>
    } else {
      return <h3>{text}</h3>
    }
  };
  
  const element = (
    <> 
      <Text text="Hi" />
      <Text text="hi" />

    </>
  );

  ReactDOM.render(element, rootElement);
</script>  
  • 다른 예시 한번 더
<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");

  const Number = ({ number }) => {
    return number % 2 === 0 ? <h1>{number}</h1> : <h3>{number}</h3>; 
  }
  
  const element = (
    <>
      <Number number={1} />
      <Number number={2} />
      <Number number={3} />
      <Number number={4} />
    </>
  );

  ReactDOM.render(element, rootElement);
</script>  

<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");

  const Number = ({ number, selected }) => {
    return selected ? <h1>{number}</h1> : <h3>{number}</h3>; 
  }
  
  const element = (
    <>
      <Number number={1} />
      <Number number={2} selected={true} />
      <Number number={3} />
      <Number number={4} />
    </>
  );

<script src="https://unpkg.com/@babel/standalone/babel.min.js">
</script>

<script type="text/babel">
  const rootElement = document.getElementById("root");

  const Number = ({ number, selected }) => {
    return selected ? <h1>{number}</h1> : <h3>{number}</h3>; 
  }
  
  const element = (
    <>
      {[1,2,3,4,5,6,7,8,9,10].map((number) => (
        <Number number={number} selected={number === 3} />  
      ))}
    </>
  );

  ReactDOM.render(element, rootElement);
</script>  

  • 이미 HTML 에서 JS,CSS 등을 쓰던 것도 Interpolation, 동일개념이다.

리랜더링

  • 바닐라JS --> 변경으로 인해 Element를 다시 그림
    • 요소를 계속 다시 생성해서 보여줌
  • React --> 변경된 부분만 다시 그림
    • 글씨가 바뀌는 요소라 하면 글자만 바뀐다. 요소는 그대로.

      reconciliation

이벤트 핸들러

<!DOCTYPE html>
<html lang="en">
<body>
<button id="button" onclick="document.getElementById('demo').innerHTML=Date()">The time is?</button>
// 인라인으로 이벤트 넣기
<p id="demo"></p>
	
<script>
	const button = document.getElementById("button");
	button.addEventListener("mouseout", () => alert("bye"));
	button.addEventListener("click", () => alert("press"));
</script>
</body>
</html>
  • React
    바닐라JS 가 전체가 소문자였다면 리액트는 카멜케이스로 표현
    on{Event} / onClick / onMouse, onFocus
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const handleClick = () => alert("pressed");
      const handleMouseOut = () => alert("bye");
      const element = (
        <button onClick={handleClick} onMouseOut={handleMouseOut}>
          Press
        </button>
      );
      ReactDOM.render(element, rootElement);
    </script>
  </body>
</html>

간단한 검색창 만들어보기

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const state = { keyword: "", typing: false, result: "" };

      const App = () => {
        function handleChange(event) {
          setState({ typing: true, keyword: event.target.value });
        }

        function handleClick() {
          setState({
            typing: false,
            result: "we find results of ${state.keyword}"
          });
        }

        return (
          <>
            <input onChange={handleChange} />
            <button onClick={handleClick}>search</button>
            <p>
              {state.typing ? `Looking for ${state.keyword}...` : state.result}
            </p>
          </>
        );
      };

      function setState(newState) {
        Object.assign(state, newState);
        render();
      }

      function rander() {
        ReactDOM.render(<App />, rootElement);
      }
    </script>
  </body>
</html>

컴포넌트 상태 다루기(useState)

컴포넌트? 앨리먼트? DOM?

  • DOM : 논리 트리
  • 컴포넌트 : 앨리먼트의 집합
  • 앨리먼트 : 컴포넌트의 구성요소

리액트 상태로 작성

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const App = () => {
        const [keyword, setKeyword] = React.useState("");
        const [result, setResult] = React.useState();
        const [typing, setTyping] = React.useState(false);

        function handleChange(event) {
          setKeyword(event.target.value);
          setTyping(true);
        }
      
        function handleClick() {
          setTyping(false);
          setResult(`We find results of ${keyword}`);
        }

        return (
          <>
            <input onChange={handleChange} />
            <button onClick={handleClick}>search</button>
            <p>{typing ? `Looking for ${keyword}...`: result}</p>
          </>
        );
      };
 
      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • const [keyword, setKeyword] = React.useState(""); 와 동일한 표현

컴포넌트 사이드 이펙트 다루기(useEffect)

사이드 이펙트?

  • 사이드 이펙트 = 부작용
  • 의도하지 않은 효과 vs 부수효과

위에서 작성한 검색에서..

  • 입력한 값이 남아있었으면 좋겠다
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const App = () => {
        const [keyword, setKeyword] = React.useState(() => {
          return window.localStorage.getItem("keyword") || "";
        );
        const [result, setResult] = React.useState("");
        const [typing, setTyping] = React.useState(false);

        React.useEffect(() => {
          window.localStorage.setItem("keyword", keyword);
        }, [keyword, typing]);  //의존성 배열 (어떤 값이 변할때 효과가 일어나게 하고 싶은가?)
      
      
        function handleChange(event) {
          window.localStorage.setItem("keyword", event.target.value);
          setKeyword(event.target.value);
          setTyping(true);
        }
      
        function handleClick() {
          setTyping(false);
          setResult(`We find results of ${keyword}`);
        }

        return (
          <>
            <input onChange={handleChange} value={keyword} />
            <button onClick={handleClick}>search</button>
            <p>{typing ? `Looking for ${keyword}...`: result}</p>
          </>
        );
      };
 
      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>

커스텀 훅

use{Name}

  • keyword, result, typing 값이 모두 저장되도록 하고 싶다.
  • 현재 아는 내용으로는 useEffect부분을 동일하게 이름따라 3번 만들어줘야 이상없이 정상 작동하게 되는 형태이다.
  • 동일 기능인데 3번 써야하는 것이 비효율적으로 느껴진다.
  • 동일 기능있을때는 함수로 빼서 합쳐주는 것이 일반적!
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      
      function useLocalStorage(itemName, value = "") {
        const [state, setState] = React.useState(() => {
          return window.localStorage.getItem(itemName) || value;
        );
      
        React.useEffect(() => {
          window.localStorage.setItem(itemName, state);
        }, [state]);
      
        return [state, setState];    // useState 같은 훅과 동일한 형태로 만들고 싶은 것
      }
      
      const App = () => {
        const [keyword, setKeyword] = useLocalStorage("keyword");
        const [result, setResult] = useLocalStorage("result");
        const [typing, setTyping] = useLocalStorage("typing", false);

        function handleChange(event) {
          setKeyword(event.target.value);
          setTyping(true);
        }
      
        function handleClick() {
          setTyping(false);
          setResult(`We find results of ${keyword}`);
        }

        return (
          <>
            <input onChange={handleChange} value={keyword} />
            <button onClick={handleClick}>search</button>
            <p>{typing ? `Looking for ${keyword}...`: result}</p>
          </>
        );
      };
 
      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>

hook flow 이해

App - Child

  • 훅의 호출타이밍을 알아보자
  • 익숙한 예제로 확인해보기
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const Child = () => {
        console.log("   Child render start");
        const [text, setText] = React.useState(() => {
          console.log("   Child useState");
          return "";
        });

        React.useEffect(() => {
          console.log("   Child useEffect, no deps");
          return () => {
            console.log("   Child useEffect [Cleanup], no deps");
          };
        });

        React.useEffect(() => {
          console.log("   Child useEffect, empty deps");
          return () => {
            console.log("   Child useEffect [Cleanup], empty deps");
          };
        }, []);

        React.useEffect(() => {
          console.log("   Child useEffect, [text]");
          return () => {
            console.log("   Child useEffect [Cleanup], [text]");
          };
        }, [text]);

        function handleChange(event) {
          setText(event.target.value);
        }

        const element = (
          <>
            <input onChange={handleChange} />
            <p>{text}</p>
          </>
        );

        console.log("   Child render end");
        return element;
      };

      const App = () => {
        console.log("APP render start");
        const [show, setShow] = React.useState(() => {
          console.log("APP useState");
          return false;
        });

        React.useEffect(() => {
          console.log("APP useEffect, no deps");

          return () => {
            console.log("APP useEffect [Cleanup], no deps");
          };
        });

        React.useEffect(() => {
          console.log("APP useEffect, empty deps");

          return () => {
            console.log("APP useEffect [Cleanup], empty deps");
          };
        }, []);

        React.useEffect(() => {
          console.log("APP useEffect, [show]");
          return () => {
            console.log("APP useEffect [Cleanup], [show]");
          };
        }, [show]);

        function handleClick() {
          setShow((prev) => !prev);
        }

        console.log("APP render end");

        return (
          <>
            <button onClick={handleClick}>Search</button>
            {show ? <Child /> : null}
          </>
        );
      };

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

리액트 Element에 스타일 입히기

style

  <body>
  <div id="root"></div>
  <script src="https://unpkg.com/react@16.12.0/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone@7.8.3/babel.js"></script>

  <style>
    .button {
      background-color: #4caf50; /* Green */
      border: none;
      color: white;
      padding: 15px 32px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 16px;
      margin: 4px 2px;
      cursor: pointer;
    }
    .blue {
      background-color: #008cba;
    } /* Blue */
    .red {
      background-color: #f44336;
    } /* Red */
    .gray {
      background-color: #e7e7e7;
      color: black;
    }
    .black {
      background-color: #555555;
    }
  </style>
  <script type="text/babel">
    function Button({ className = "", color, style, ...rest }) {
      return (
        <button
          className={`button ${className}`}
          style={{ backgroundColor: color, borderRadius: 8, ...style }}
          {...rest}
        />
      );
    }

    const element = (
      <>
        <Button style={{ borderRadius: "50%" }}>Green</Button>
        <Button color="blue">Blue</Button>
        <Button color="red">Red</Button>
        <Button color="gray">Gray</Button>
        <Button color="black">Black</Button>
      </>
    );

    ReactDOM.render(element, document.getElementById("root"));
  </script>
</body>
</html>

Ref로 Dom 다루기

  • 강제로 id를 줘서 찾아 넣으면 원하는 동작을 할 수 있다.
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const App = () => {
        React.useEffect({
          document.getElementById("input").focus();
        },[]);
        return (
          <>
            <input id="input" />
          </>;
        );
      };
      
      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • useRef라는 hook을 써보자
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const App = () => {
        const inputRef = React.useRef();
        React.useEffect(() => {
          inputRef.current.focus();   // useRef를 이용할때는 current라는 것을 쓰고 후에 메서드 줄 수 있다. 
         
        },[]);
      
        return (
          <>
            <input ref={inputRef} />
          </>;
        );
      };
      
      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const App = () => {
        const inputRef = React.useRef();
        const divRef = React.useRef();
      
        React.useEffect(() => {
          inputRef.current.focus(); 
          
          setTimeout(() => {
            divRef.current.style.backgroundColor = "pink";
          },1000);
        },[]);
      
        return (
          <>
            <input ref={inputRef} />
            <div
              ref={divRef}    
              style={{ height:100, width: 300, backgroundColor: "brown" }} 
            />
          </>;
        );
      };
      
      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>

Why React?

document.getElementById 류를 사용하지 않고.
useRef라는 별도의 방법을 제공할까?

  • React 자체의 틀안에서 관리가능하게 하기 위해서.
  • "레퍼런스" <- 이름 언급해서 스타일이나 메서드 줄 수 있다.

Form 다루기

  • React 자체적으로 만들어서 사용할 수도 있지만,
    이미 htmlForm으로 잘 만들어 진 것을 가져와서 사용 하는 것이 좋을 수 있다.
  • 연습 1
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const App = () => {
        const handleSubmit = {event} => {
          event.preventDefault();  // 디폴트 동작 하지 말아라 <- 새로고침처럼 깜빡이는거 안하게 됨
          console.dir(event.target); // 콘솔창에 원하는 값 전체구조를 확인하게 해준다.
          alert(
            `FirstName : ${event.target[0].value}, LastName : ${event.target[1].value}`
          );                    // 결국 이걸로도 가능했으나 위치 순서 뒤바뀔 것 같고 불안한 상태. 
        ----------------------------------------------------------------------------------
        const handleSubmit = {event} => {
          event.preventDefault();
          console.dir(event.target.elements);  // 이안에도 FN, LN 있는 것 확인함. 써보자 
          alert(`FirstName : ${event.target.elements.fname.value}, 
                  LastName : ${event.target.elements.lname.value}`
          );     // 잘 작동하며, 구조에서 키로 찍혀있는 부분 가져다 사용하는 것 알아둘 것!
        
        };
        return ( 
          <form onSubmit={handleSubmit}>
            <label htmlFor="fname">First name:</label>
            <br />
            <input type="text" id="fname" name="fname" defaultValue="John" />
            <br />
            <label htmlFor="lname">Last name:</label>
            <br />
            <input type="text" id="lname" name="lname" defaultValue="Doe" />
            <br />
            <br />
            <input type="submit" value="Submit" />
          </form>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • 연습 2
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const App = () => {
        const handleSubmit = {event} => {
          event.preventDefault();  // 디폴트 동작 하지 말아라 <- 새로고침처럼 깜빡이는거 안하게 됨
          console.dir(event.target); // 콘솔창에 원하는 값 전체구조를 확인하게 해준다.
          alert(
            `FirstName : ${event.target[0].value}, LastName : ${event.target[1].value}`
          );                    // 결국 이걸로도 가능했으나 위치 순서 뒤바뀔 것 같고 불안한 상태. 
        ----------------------------------------------------------------------------------
        const handleSubmit = {event} => {
          event.preventDefault();
          console.dir(event.target.elements);
      
          alert(`FirstName : ${event.target.elements.fname.value}, 
               Choosed Car : ${event.target.elements.cars.value}`
          );     
        };
        return ( 
          <form onSubmit={handleSubmit}>
            <label htmlFor="fname">First name:</label>
            <br />
            <input type="text" id="fname" name="fname" defaultValue="John" />
            <br />
            <label htmlFor="cars">Choose a car:</label>
            <select id="cars" name="cars">
              <option value="volvo">Volvo</option>
              <option value="saab">Saab</option>
              <option value="fiat">Fiat</option>
              <option value="audi">Audi</option>
            </select>
            <br />
            <br />
            <input type="submit" value="Submit" />
          </form>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • 연습 3
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const App = () => {
        const [message, setMessage] = React.useState("");
        const [phoneNumber, setPhoneNumber] = React.useState("");

        const handleSubmit = (event) => {
          event.preventDefault();
          alert(phoneNumber);
        };
      
        const handleChange = (event) => {
          if (event.target.value.startsWith(0)) {
            setMessage("phone Number is valid");
            setPhoneNumber(event.target.value);
          } else if (event.tartget.value.length === 0) {
            setPhoneNumber("");
            setMessage("");
          } else {
            setMessage("phone Number should starts with 0");
          }
          
        };
      
        return (
          <form onSubmit={handleSubmit}>
          	<label htmlFor="phone">Phone Number: </label>
          	<br />
          	<input 
              id="phone" 
              name="phone" 
              onChange={handleChange}
              value={phoneNumber}
            />
            <p>{message}</p>
          	<br />
          	<br />
          	<button 
              type="submit" 
              disabled={
                phoneNumber.length === 0 || message !== "Phone Number is valid"
              }
            >
              Submit
            </button>
            <p>{phoneNumber}</p>
          </form>  
        );
      };

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

Error 다루기

catch Error

try{
  [에러가 날수도 있는 코드]
} catch (error) {
  [에러가 났을 때 어떻게 처리할지]
}
  • Child가 에러를 뿜도록 잡아놓은 상태
  • 캐치되지 않은 에러는 오류 뿜고 html의 Component를 그리지조차 못한다.
  • ErrorBoundary로 감싸면 그 안에서 나는 에러 캐치해서 관리 가능
<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      class ErrorBoundary extends React.Component {
        state = { error: null };
        static getDerivedStateFromError(error) {
          return { error };
        }

        render() {
          const { error } = this.state;
          if (error) {
            return <this.props.fallback error={error} />;
          }

          return this.props.children;
        }
      }

      const Child = () => {
        throw new Error("Something Wrong....");
        return <p>Child...</p>;
      };

      const Fallback = ({ error }) => {
        alert(error.message);
        return <p>There is some ERROR...</p>;
      };

      const App = () => {
        return (
          <>
            <p>App</p>
            <ErrorBoundary fallback={Fallback}>
              <Child />
            </ErrorBoundary>
          </>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • Fallback ----> Error가 났을 때 보여줄 컴포넌트!!

Key와 리렌더링 알아보기

  • Key - Value 쌍으로 다룬다
  • DB, Dictionary, Json, Object 등에서 볼 수 있다.
  • Key는 Value를 특정하는 이름
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const todos = [
        { id: 1, value: "Wash dishes" },
        { id: 2, value: "Clean the bed" },
        { id: 3, value: "Running" },
        { id: 4, value: "Learning" }
      ];
      const App = () => {
        const[items, setItems] = React.useState(todos);
      
        const handleDoneClick = (todo) => {
          setItems((items) => items.filter((item) => item !== todo));
        };
      
        const handleRestoreClick = () => {
          setItems((items) => [
            ...items, 
            todos.find((item) => !items.includes(item))
          ]);
        };
      
        return (
          <>
            {items.maps((todo) => (
              <div key={todo.id}>
                <span>{todo.value}</span>
                <button onClick={() => handleDoneClick(todo)}>Done</button>
              </div> 
            ))}
            <button onClick={handleRestoreClick}>Restore</button>
          </>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • value를 특정할 수 있는 고유값이 꼭 필요하다. 특히!!! map등을 할때 꼭 넣도록 하자.

Detail

  • key가 중요한 이유는 재사용 때문이다.
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const todos = [
        [
          { id: 1, value: "Wash dishes" },
          { id: 2, value: "Clean the bed" },
          { id: 3, value: "Running" },
          { id: 4, value: "Learning" }
        ],
        [
          { id: 4, value: "Learning" },
          { id: 1, value: "Wash dishes" },
          { id: 2, value: "Clean the bed" },
          { id: 3, value: "Running" }
        ],
        [
          { id: 3, value: "Running" },
          { id: 4, value: "Learning" },
          { id: 1, value: "Wash dishes" },
          { id: 2, value: "Clean the bed" }
        ],
        [
          { id: 2, value: "Clean the bed" },
          { id: 4, value: "Learning" },
          { id: 3, value: "Running" },
          { id: 1, value: "Wash dishes" }                    
        ]
      ];
      const App = () => {
        const[items, setItems] = React.useState(todos[0]);
      
        React.useEffect(() => {
          const interval = setInterval(() => {
            const random = Math.floor(Math.random() * 3);
            setItems(todos[random]);
          }, 1000);
      
          return () => {
            clearInterval(interval);
          };
        }, []);
      
        const handleDoneClick = (todo) => {
          setItems((items) => items.filter((item) => item !== todo));
        };
      
        const handleRestoreClick = () => {
          setItems((items) => [
            ...items, 
            todos.find((item) => !items.includes(item))
          ]);
        };
      
        return (
          <>
            {items.maps((todo, index) => (
              <div key={todo.id}>
                <button onClick={() => handleDoneClick(todo)}>
                  {todo.value}
                </button>
              
              </div> 
            ))}
            <br />
            <br />
            <button onClick={handleRestoreClick}>Restore</button>
          </>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • key값을 안주면 버튼은 그대로 있고 글자가 새로 바뀐 것 같은 상태
    • 제대로 재사용된 것은 아니다.
    • index 같이 계속 변화하는 값은 key로는 부적합하다(가능은 하다!)
      (재배열 되지 않는 경우에는 괜찮, 재배열 되면 부적합)

  • key값을 주면 요소들이 통째로 이동되는 것 같은 효과.

재조정(Reconciliation) 개념 더 체크해보기.
react문서에서 확인

상태 끌어올리기

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

      const Id = () => {
        const [id, setId] = React.useState("");
       
        const handleIdChange = (event) => {
          setId(event.target.value);
          console.log(`length: ${event.target.value.length}`);
        };
        return (
          <>
            <label>ID: </label>
            <input onChange={handleIdChange}/>
          </>
        );
      };
      
      const Password = () => {
        const [password, setPassword] = React.useState("");
       
        const handlePasswordChange = (event) => {
          setPassword(event.target.value);
          console.log(`length: ${event.target.value.length}`);
        };
        return (
          <>
            <label>PW: </label>
            <input type="password" 
                   onChange={handlePasswordChange} />
          </>
        );
      }; 
      const App = () => {
        const handleLoginClick = () => {
          alert(`id: ${id}, pw: ${password}`);
        };
    
        return (
          <>
            <Id />
            <br />
            <Password />
            <button disabled={true} 
                    onClick={handleLoginClick}>
              LOGIN
            </button>
          </>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • 구조
    • App(부모) -> Id, Password(자식들, 서로는 형제관계)
  • 자식component상태, 형제component 간 상태를 알수가 없다.
    • 그래서 끌어올리기 개념이 필요하다.

부모쪽으로 끌어올려놔보자.

<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
                   # App에서 전달해준 것을 받는 형태로 변경!
      const Id = ( { handleIdChange } ) => {
        # 있던 것들이 App으로!
        return (
          <>
            <label>ID: </label>
            <input onChange={handleIdChange}/>
          </>
        );
      };
      
      const Password = ( { handlePasswordChange } ) => {
        
        return (
          <>
            <label>PW: </label>
            <input type="password" 
                   onChange={handlePasswordChange} />
          </>
        );
      }; 
      const App = () => {
        const [id, setId] = React.useState("");
        const [password, setPassword] = React.useState("");
        
        const handleIdChange = (event) => {
          setId(event.target.value);
          console.log(`id length: ${event.target.value.length}`);
        };
        const handlePasswordChange = (event) => {
          setPassword(event.target.value);
          console.log(`password length: ${event.target.value.length}`);
        };
    
        const handleLoginClick = () => {
          alert(`id: ${id}, pw: ${password}`);
        };
    
        return (
          <>
            <Id handleIdChange={handleIdChange} /> # 자식으로 전달
            <br />    # 자식으로 전달
            <Password handlePasswordChange={handlePasswordChange}/>
            <button disabled={id.length ===0 || password.length === 0} 
                    onClick={handleLoginClick}>
              LOGIN
            </button>
          </>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • 이렇게 처리하면 App이 자식Conponent상태들을 알게 되었다.
    • 여러가지 조정이 가능하게 되었다.
    • 자식 바로위 부모에게 상태값을 올려놓고 사용하는 것 기억!!
    • 결국 handle에 들어갈 것은 set부분만 있으면 되고 그것만 넘겨주면 된다.

lifting up <-> drilling 좀 더 찾아봐도 좋을 것.

  • 데이터는 단방향으로만 흐르게 하고, 상태 파악이 쉽게 만드는 게 관건!

데이터 Fetch 해보기

API call

  • 서버가 데이터를 주고, 프론트가 데이터를 받아서 웹에 그려지는 형태가 될 것.
  • 이런 네트워크 통신의 한부분 -> Fetch API : 네트워크 통신 도구
    문서 확인해 보기

  • 가상 api(or web상에 고정되어있는 json)를 이용해서 예시로 만들어보자.
<html lang="en">
  <body>
    <div id="root"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const App = () => {
        const [data, setData] = React.useState(null);
        const [error, setError] = React.useState(null);
      
        React.useEffect(() => {
          fetch("http://exaple.api.url")
            .then(function (response) {
              return response.json();
            })
            .then(function (myJson) {
              setData(myJson.data);
            })
            .catch((error) => {      #.catch로 error 잡을 수 있다.
              setError(error.message);
              alert(error);
            }); 
        }, []);
      
        if (error != null) {
          return <p>There is a error!</p>;
        }
      
        if (data == null) {
          return <p>Loading....</p>;
        }
      
        return (
          <div>
            <p>People</p>
            {data.people.map((person) => (
              <div>
                <span>name: {person.name} </span>
                <span>age: {person.age}</span>
              </div>            
            ))}
          </div>
        );
      };

      ReactDOM.render(<App />, rootElement);
    </script>
  </body>
</html>
  • 여러가지로 상황별 핸들링 가능하다 ( 로딩 / 데이터 / 에러 )

출처 : fastcampus_React & Redux로 시작하는 웹 프로그래밍

profile
이것저것 합니다.

0개의 댓글