[WEEK 14] 나만의 무기 만들기 - React 강의

신호정 벨로그·2021년 11월 5일
0

Today I Learned

목록 보기
77/89

BASICS OF REACT

2.1 BEFORE REACT

<!DOCTYPE html>
<html>
    <body>
        <span>CLICK COUNTS: 0</span>
        <button id="btn">CLICK THIS BUTTON</button>
    </body>
    <script>
        let counter = 0;
        const button = document.getElementById("btn");
        const span = document.querySelector("span");

        function handleClick() {
            counter = counter + 1;
            span.innerText = `CLICK COUNTS: ${counter}`;
        }

        button.addEventListener("click", handleClick);
    </script>
</html>
  1. HTML을 만든다.

  2. JavaScript를 작성한다. (getElementById, querySelector)

  3. 이벤트를 감지한다. (addEventListener)

  4. 데이터를 업데이트한다. (counter)

  5. HTML을 업데이트한다. (span.innerText)

2.2 REACT ELEMENTS

https://unpkg.com/react@17.0.2/umd/react.production.min.js
https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js

ReactDOM.render()

render()은 리액트 엘리먼트를 HTML로 만들어 배치한다는 것을 의미한다.

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <div id="root">
            <span></span>
        </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");
        const span = React.createElement(
            "span",
            { id: "sexy-span", style: { color: "red" } },
            "Hello World"
        );

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

2.3 EVENTS IN REACT

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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");
        const h3 = React.createElement(
            "h3",
            {
                id: "title",
                onMouseEnter: () => console.log("MOUSE ENTER")
            },
            "HELLO WORLD"
        );
        const btn = React.createElement(
            "button",
            {
                onClick: () => console.log("CLICKED"),
                style: {
                    backgroundColor: "grey"
                }
            },
            "CLICK THIS BUTTON"
        );
        const container = React.createElement("div", null, [h3, btn]);

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

2.4 RECAP

React JS와 ReactDOM을 import한다.

React JS는 엘리먼트를 생성하고 이벤트 리스너를 더할 수 있도록 한다.

ReactDOM은 엘리먼트들을 HTML에 렌더링할 수 있도록 한다.

ReactDOM은 container 엘리먼트를 body의 div id="root"는 렌더링하도록 한다.

2.5 JSX PART. 1

JSX는 JavaScript를 확장한 문법이다.

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 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 WORLD
            </h3>
        );
        const Button = <button style={{
            backgroundColor: "green",
            }} onClick={ () => console.log("CLICKED") }>
            CLICK THIS BUTTON
            </button>;
        const container = React.createElement("div", null, [Title, Button]);

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

2.6 JSX PART. 2

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function Title() {
            return <h3 id="title" onMouseEnter={ () => console.log("MOUSE ENTER") }>
                HELLO WORLD
            </h3>
        };

        function Button() {
            return <button style={{
            backgroundColor: "green",
            }}
            onClick={ () => console.log("CLICKED")
            }>
            CLICK THIS BUTTON
            </button>
        };

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

3. STATES

3.0 UNDERSTANDING STATES

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        let counter = 0;

        function render() {
            ReactDOM.render(<Container />, root);
        }

        function countUp() {
            counter = counter + 1;
            // ReactDOM.render(<Container />, root);
            render();
        }

        function Button() {
            return <button
                style={{
                    backgroundColor: "green",
                }}
                onClick={ () => console.log("CLICKED")
            }
            >
            CLICK THIS BUTTON
            </button>
        }

        function Container() {
            return (
                <div>
                    <h3>CLICK COUNT: { counter }</h3>
                    <button onClick={ countUp }>CLICK THIS BUTTON</button>
                </div>
            );
            }

        render();
    </script>
</html>

3.1 useState() PART. 1

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function render() {
            ReactDOM.render(<Container />, root);
        }

        let counter = 0;

        function App() {
            const [counter, modifier] = React.useState(0);
            counter = 0;
            return (
                <div>
                    <h3>CLICK COUNT: { counter }</h3>
                    <button>CLICK THIS BUTTON</button>
                </div>
            );
        }

        render();
    </script>
</html>

3.2 useState() PART. 2

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function render() {
            ReactDOM.render(<App />, root);
        }

        function App() {
            const [counter, setCounter] = React.useState(0);
            const onClick = () => {
                setCounter(counter + 1);
            };

            return (
                <div>
                    <h3>CLICK COUNT: { counter }</h3>
                    <button onClick={ onClick }>CLICK THIS BUTTON</button>
                </div>
            );
        }

        render();
    </script>
</html>

3.3 RECAP

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function render() {
            ReactDOM.render(<App />, root);
        }

        function App() {
            const [counter, setCounter] = React.useState(0);
            const onClick = () => {
                setCounter(counter + 1);
            };
            console.log("Rendered");
            console.log(counter);

            return (
                <div>
                    <h3>CLICK COUNT: { counter }</h3>
                    <button onClick={ onClick }>CLICK THIS BUTTON</button>
                </div>
            );
        }

        render();
    </script>
</html>

3.4 STATE FUNCTIONS

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function render() {
            ReactDOM.render(<App />, root);
        }

        function App() {
            const [counter, setCounter] = React.useState(0);
            const onClick = () => {
                setCounter((current) => current + 1);
            };
            console.log("Rendered");
            console.log(counter);

            return (
                <div>
                    <h3>CLICK COUNT: { counter }</h3>
                    <button onClick={ onClick }>CLICK THIS BUTTON</button>
                </div>
            );
        }

        render();
    </script>
</html>

3.5 INPUTS AND STATES PART. 1

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        const root = document.getElementById("root");

        function render() {
            ReactDOM.render(<App />, root);
        }

        function App() {
            const [minutes, setMinutes] = React.useState();
            const onChange= () => {
                console.log("UPDATED");
                setMinutes(event.target.value);
            };

            return (
                <div>
                    <h1 className="title">TIME CONVERTER</h1>
                    <label htmlFor="minutes"> MINUTES </label>
                    <input
                        id="minutes"
                        value={ minutes }
                        placeholder="MINUTES"
                        type="number"
                        onChange={ onChange }
                    />
                    <h4>MINUTES TO CONVERT: { minutes }</h4>
                    <label htmlFor="hours"> HOURS </label>
                    <input id="hours" placeholder="hours" type="number" />
                </div>
            );
        }

        render();
    </script>
</html>

3.6 INPUTS AND STATES PART. 2

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        function App() {
            const [time, setTime] = React.useState();
            const [inverted, setInverted] = React.useState(false);
            const onChange= (event) => {
                console.log("UPDATED");
                setTime(event.target.value);
            };
            const reset = () => setTime(0);
            const onInvert = () => {
                reset();
                setInverted((current) => !current);
            }

            return (
                <div>
                    <div>
                        <h1 className="title">TIME CONVERTER</h1>
                        <label htmlFor="minutes"> MINUTES </label>
                        <input
                            id="minutes"
                            type="number"
                            value={ inverted ? (time * 60) : time }
                            placeholder="MINUTES"
                            onChange={ onChange }
                            disabled={ inverted }
                        />
                    </div>
                    <div>
                        <label htmlFor="hours"> HOURS </label>
                        <input
                            id="hours"
                            type="number"
                            value={ inverted ? time : Math.round(time / 60) }
                            placeholder="HOURS"
                            onChange = { onChange }
                            disabled={ !inverted }
                        />
                    </div>
                    <p>
                        <button onClick={ reset }>RESET</button>
                        <button onClick={ onInvert }>INVERT</button>
                    </p>
                </div>
            );
        }

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

3.7 INPUTS AND STATES PART. 3

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        function TimeConverter() {
            const [time, setTime] = React.useState();
            const [inverted, setInverted] = React.useState(false);
            const onChange = (event) => {
                console.log("UPDATED");
                setTime(event.target.value);
            };
            const reset = () => setTime(0);
            const onInvert = () => {
                reset();
                setInverted((current) => !current);
            };

            return (
                <div>
                    <div>
                        <h1 className="title">TIME CONVERTER</h1>
                        <label htmlFor="minutes"> MINUTES </label>
                        <input
                            id="minutes"
                            type="number"
                            value={ inverted ? (time * 60) : time }
                            placeholder="MINUTES"
                            onChange={ onChange }
                            disabled={ inverted }
                        />
                    </div>
                    <div>
                        <label htmlFor="hours"> HOURS </label>
                        <input
                            id="hours"
                            type="number"
                            value={ inverted ? time : Math.round(time / 60) }
                            placeholder="HOURS"
                            onChange = { onChange }
                            disabled={ !inverted }
                        />
                    </div>
                    <p>
                        <button onClick={ reset }> RESET </button>
                        <button onClick={ onInvert }> INVERT </button>
                    </p>
                </div>
            );
        }
        
        function DistanceConverter() {
            const [distance, setDistance] = React.useState();
            const [inverted, setInverted] = React.useState(false);
            const onChange = (event) => {
                console.log("UPDATED");
                setDistance(event.target.value);
            };
            const reset = () => setDistance(0);
            const onInvert = () => {
                reset();
                setInverted((current) => !current);
            };

            return (
                <div>
                    <div>
                        <h1 className="title">DISTANCE CONVERTER</h1>
                        <label htmlFor="kilometers"> KILOMETERS </label>
                        <input
                            id="kilometers"
                            type="number"
                            value={ inverted ? (distance * 1.6) : distance }
                            placeholder="KILOMETERS"
                            onChange={ onChange }
                            disabled={ inverted }
                        />
                    </div>
                    <div>
                        <label htmlFor="miles"> MILES </label>
                        <input
                            id="miles"
                            types="number"
                            value={ inverted ? distance : Math.round(distance / 1.6) }
                            placeholder="MILES"
                            onChange={ onChange }
                            disabled={ !inverted }
                        />
                    </div>
                    <p>
                        <button onClick={ reset }> RESET </button>
                        <button onClick={ onInvert }> INVERT </button>
                    </p>
                </div>
            );
        }

        function App() {
            const [index, setIndex] = React.useState(0);
            const onSelect = (event) => {
                console.log(event.target.value);
                setIndex(event.target.value);
            }

            return (
                <div>
                    <h1 className="title">VERSATILE CONVERTER</h1>
                    <select value={ index } onChange={ onSelect }>
                        <option value="0">DEFAULT</option>
                        <option value="1">TIME CONVERTER</option>
                        <option value="2">DISTANCE CONVERTER</option>
                    </select>
                    <hr />
                    { index === "1" ? <TimeConverter /> : null }
                    { index === "2" ? <DistanceConverter /> : null }
                </div>
            );
        }

        const root = document.getElementById("root");
        ReactDOM.render(<App />, root);
    </script>
</html>

4. PROPS

4.0 PROPS

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        function Button(props) {
            console.log(props.text, props.isLarge);

            return (
                <button
                    style={{
                        backgroundColor: "grey",
                        color: "white",
                        padding: "10px 20px",
                        border: 0,
                        borderRadius: 10,
                        fontSize: props.isLarge ? 20 : 10
                    }}
                >
                { props.text }
                </button>
            );
        }

        function App() {
            return (
                <div>
                    <Button text="SAVE" isLarge={ false } />
                    <Button text="CONFIRM" isLarge={ true } />
                </div>
            );
        }

        const root = document.getElementById("root");
        ReactDOM.render(<App />, root);
    </script>
</html>

4.2 MEMO

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        function Button({ text, changeValue, fontSize }) {
            console.log(text, "RENDERED")
            return (
                <button
                    style={{
                        backgroundColor: "grey",
                        color: "white",
                        padding: "10px 20px",
                        border: 0,
                        borderRadius: 10,
                        fontSize: fontSize
                    }}
                    onClick={ changeValue }
                >
                    { text }
                </button>
            );
        }

        function App() {
            const [value, setValue] = React.useState("SAVE");
            const changeValue = () => setValue("REVERT");
            return (
                <div>
                    <MemorizedButton text={ value } changeValue={ changeValue } fontSize={ 20 } />
                    <MemorizedButton text="CONFIRM" fontSize={ 20 } />
                </div>
            );
        }

        const MemorizedButton = React.memo(Button);

        const root = document.getElementById("root");
        ReactDOM.render(<App />, root);
    </script>
</html>

4.3 PROPS RECAP

<!DOCTYPE html>
<html>
    <head></head>
    <body style>
        <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 src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        function Button({ text, changeValue, fontSize }) {
            console.log(text, "RENDERED")
            return (
                <button
                    style={{
                        backgroundColor: "grey",
                        color: "white",
                        padding: "10px 20px",
                        border: 0,
                        borderRadius: 10,
                        fontSize: fontSize
                    }}
                    onClick={ changeValue }
                >
                    { text }
                </button>
            );
        }

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

        function App() {
            const [value, setValue] = React.useState("SAVE");
            const changeValue = () => setValue("REVERT");
            return (
                <div>
                    <MemorizedButton text={ value } changeValue={ changeValue } fontSize={ 20 } />
                    <MemorizedButton text="CONFIRM" fontSize={ 20 } />
                </div>
            );
        }

        const MemorizedButton = React.memo(Button);

        const root = document.getElementById("root");
        ReactDOM.render(<App />, root);
    </script>
</html>

5. CREATE-REACT-APP

5.0 CRA INTRODUCTION

import Button from "./Button.js";
import styles from "./App.module.css";

function App() {
  return (
    <div>
      <h1 className={styles.title}>CREATE-REACT-APP</h1>
      <Button text={"CONTINUE"} />
    </div>
  );
}

export default App;
import PropTypes from "prop-types";
import styles from "./Button.module.css";

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

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

export default Button;
.button {
    color: white;
    background-color: lightslategrey;
    border: 1;
    border-radius: 10px;
    font-size: large;
}
.title {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    font-size: 20px;
}

6. EFFECTS

6.0 EFFECTS INTRODUCTION

import React, { useState } from "react";

function App() {
  const [counter, setValue] = React.useState(0);
  const onClick = () => setValue((prev) => prev + 1);
  console.log("RENDERED");

  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>COUNTER</button>
    </div>
  );
}

export default App;

6.1 useEffect()

import React, { useState, useEffect } from "react";

function App() {
  const [counter, setValue] = React.useState(0);
  const onClick = () => setValue((prev) => prev + 1);
  console.log("RENDERED CONSTANTLY");
  const renderTemporary = () => {
    console.log("RENDERED TEMPORARILY");
  }
  useEffect(renderTemporary, []);

  return (
    <div>
      <h1>{counter}</h1>
      <button onClick={onClick}>COUNTER</button>
    </div>
  );
}

export default App;

6.2 DEPS

import React, { useState, useEffect } from "react";

function App() {
  const [counter, setValue] = React.useState(0);
  const [keyword, setKeyword] = React.useState("");
  const onClick = () => setValue((prev) => prev + 1);
  const onChange = (event) => setKeyword(event.target.value);
  // console.log("RENDERED CONSTANTLY");
  useEffect(() => {
    console.log("RENDERED TEMPORARILY");
  }, []);
  useEffect(() => {
    console.log("COUNTER UPDATED");
  }, [counter]);
  useEffect(() => {
    console.log("SEARCHED", keyword);
  }, [keyword]);
  // useEffect(() => {
  //   console.log("COUNTER UPDATED AND SEARCHED");
  // }, [counter, keyword]);

  return (
    <div>
      <input
        type="text"
        value={keyword}
        placeholder="SEARCH"
        onChange={onChange}
      />
      <h1>{counter}</h1>
      <button onClick={onClick}>COUNTER</button>
    </div>
  );
}

export default App;

6.4 CLEANUP FUNCTION

import React, { useState, useEffect } from "react";

function EffectFunc() {
  useEffect(function () {
    console.log("CREATED");
    return function () {
      console.log("ELIMINATED");
    };
  }, []);

  return <h1>HELLO</h1>;
}

function App() {
  const [showing, setShowing] = useState(false);
  const onClick = () => setShowing((prev) => !prev);

  return (
    <div>
      {showing ? <EffectFunc /> : null}
      <button onClick={onClick}>{showing ? "HIDE" : "SHOW"}</button>
    </div>
  );
}

export default App;

7. PRACTICE

7.0 EXERCISE: TO-DO LIST

import { useState } from "react";

function App() {
  const [todo, setTodo] = useState("");
  const [todoList, setTodoList] = useState([]);
  const onChange = (event) => setTodo(event.target.value);
  const onSubmit = (event) => {
    event.preventDefault();
    if (todo === "") {
      return;
    }
    setTodoList(currentTodoList => [todo, ...currentTodoList])
    setTodo("");
  };
  console.log(todoList);

  return (
    <div>
      <h1>TO-DO LIST ({todoList.length})</h1>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          placeholder="FILL IN YOUR LIST"
          value={todo}
          onChange={onChange}
        />
        <button>ADD TO-DO</button>
      </form>
    </div>);
}

export default App;

7.2 EXERCISE: COIN TRACKER

import { useState, useEffect } from "react";

function App() {
  const [loading, setLoading] = useState(true);
  const [crypto, setCrypto] = useState([])
  useEffect(() => {
    fetch("https://api.coinpaprika.com/v1/tickers")
      .then((response) => response.json())
      .then((json) => {
        setCrypto(json);
        setLoading(false);
      });
  }, []);

  return (
    <div>
      <h1>CRYPTOCURRENCY {loading ? "" : `(${crypto.length})`}</h1>
      {loading ? (
        <strong>LOADING...</strong>
      ) : (
        <select>
          {crypto.map((crypto) => (
            <option>
              {crypto.name} ({crypto.symbol}): ${crypto.quotes.USD.price}
            </option>
          ))}
        </select>
      )}
    </div>
  );
}

export default App;

73. MOVIE APPLICATION PART. 1

import { useState, useEffect } from "react";

function App () {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  const getMovies = async() => {
    const json = await (await fetch(
      `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
      )
    ).json();
    setMovies(json.data.movies);
    setLoading(false);
  }
  useEffect(() => {
    getMovies();
  }, []);
  console.log(movies);

  return (
    <div>
      {loading ? (
      <h1>LOADING...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <div key={movie.id}>
              <img src={movie.medium_cover_image} />
              <h2>{movie.title}</h2>
              <p>{movie.summary}</p>
              <ul>
                {movie.genres.map((genre) => (
                  <li key={genre}>{genre}</li>
                ))}
              </ul>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

export default App;

7.4 MOVIE APPLICATION PART. 2

function App () {
  return null;
}

export default App;
import { useState, useEffect } from "react";
import Movie from "../components/Movie.js"

function Home() {
    const [loading, setLoading] = useState(true);
    const [movies, setMovies] = useState([]);
    const getMovies = async () => {
        const json = await (await fetch(
            `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
        )
        ).json();
        setMovies(json.data.movies);
        setLoading(false);
    }
    useEffect(() => {
        getMovies();
    }, []);
    console.log(movies);

    return (
        <div>
            {loading ? (
                <h1>LOADING...</h1>
            ) : (
                <div>
                    {movies.map((movie) => (
                        <Movie
                            key={movie.id}
                            coverImage={movie.medium_cover_image}
                            title={movie.title}
                            summary={movie.summary}
                            genres={movie.genres}
                        />
                    ))}
                </div>
            )}
        </div>
    );
}

export default Home;
import PropTypes from "prop-types";

function Movie({ coverImage, title, summary, genres }) {
    return (
        <div>
            <img src={coverImage} alt={title} />
            <h2>{title}</h2>
            <p>{summary}</p>
            <ul>
                {genres.map((genre) => (
                    <li key={genre}>{genre}</li>
                ))}
            </ul>
        </div>
    );
}

Movie.propTypes = {
    coverImage: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    summary: PropTypes.string.isRequired,
    genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default Movie;
function Detail() {
    return <h1>Detail</h1>;
}

export default Detail;

7.5 REACT ROUTER

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/movies" element={<Detail />} />
      </Routes>
    </Router>
  );
}

export default App;
import { useState, useEffect } from "react";
import Movie from "../components/Movie.js"

function Home() {
    const [loading, setLoading] = useState(true);
    const [movies, setMovies] = useState([]);
    const getMovies = async () => {
        const json = await (
            await fetch(
                `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
            )
        ).json();
        setMovies(json.data.movies);
        setLoading(false);
    }
    useEffect(() => {
        getMovies();
    }, []);
    console.log(movies);

    return (
        <div>
            {loading ? (
                <h1>LOADING...</h1>
            ) : (
                <div>
                    {movies.map((movie) => (
                        <Movie
                            key={movie.id}
                            coverImage={movie.medium_cover_image}
                            title={movie.title}
                            summary={movie.summary}
                            genres={movie.genres}
                        />
                    ))}
                </div>
            )}
        </div>
    );
}

export default Home;
function Detail() {
    return <h1>Detail</h1>;
}

export default Detail;
import { Link } from "react-router-dom";
import PropTypes from "prop-types";

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

Movie.propTypes = {
    coverImage: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    summary: PropTypes.string.isRequired,
    genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default Movie;

7.6 PARAMETERS

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Details from "./routes/Details";

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/movies/:id" element={<Details />} />
      </Routes>
    </Router>
  );
}

export default App;
import { useState, useEffect } from "react";
import Movie from "../components/Movie.js"

function Home() {
    const [loading, setLoading] = useState(true);
    const [movies, setMovies] = useState([]);
    const getMovies = async () => {
        const json = await (
            await fetch(
                `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
            )
        ).json();
        setMovies(json.data.movies);
        setLoading(false);
    }
    useEffect(() => {
        getMovies();
    }, []);
    console.log(movies);

    return (
        <div>
            {loading ? (
                <h1>LOADING...</h1>
            ) : (
                <div>
                    {movies.map((movie) => (
                        <Movie
                            key={movie.id}
                            id={movie.id}                            
                            title={movie.title}
                            summary={movie.summary}
                            genres={movie.genres}
                            coverImage={movie.medium_cover_image}
                        />
                    ))}
                </div>
            )}
        </div>
    );
}

export default Home;
import { useEffect } from "react";
import { useParams } from "react-router-dom";

function Details() {
    const { id } = useParams();
    const getMovie = async () => {
        const json = await (
            await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
        ).json();
        console.log(json);
    };
    useEffect(() => {
        getMovie();
    }, []);

    return <h1>MOVIE DETAILS</h1>;
}

export default Details;
import { Link } from "react-router-dom";
import PropTypes from "prop-types";

function Movie({ id, title, summary, genres, coverImage }) {
    return (
        <div>
            <img src={coverImage} alt={title} />
            <h2>
                <Link to={`/movies/${id}`}>{title}</Link>
            </h2>
            <p>{summary}</p>
            <ul>
                {genres.map((genre) => (
                    <li key={genre}>{genre}</li>
                ))}
            </ul>
        </div>
    );
}

Movie.propTypes = {
    id: PropTypes.number.isRequired,
    title: PropTypes.string.isRequired,
    summary: PropTypes.string.isRequired,
    genres: PropTypes.arrayOf(PropTypes.string).isRequired,
    coverImage: PropTypes.string.isRequired,
};

export default Movie;

7.7 PUBLISHING

  1. index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import "./styles.css";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
  1. App.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Details from "./routes/Details";

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/movies/:id" element={<Details />} />
      </Routes>
    </Router>
  );
}

export default App;
  1. Home.js
import { useState, useEffect } from "react";
import Movie from "../components/Movie.js";
import styles from "./Home.module.css";

function Home() {
    const [loading, setLoading] = useState(true);
    const [movies, setMovies] = useState([]);
    const getMovies = async () => {
        const json = await (
            await fetch(
                `https://yts.mx/api/v2/list_movies.json?minimum_rating=9.0&sort_by=year`
            )
        ).json();
        setMovies(json.data.movies);
        setLoading(false);
    }
    useEffect(() => {
        getMovies();
    }, []);
    console.log(movies);

    return (
        <div className={styles.container}>
            {loading ? (
                <div className={styles.loader}>
                    <span>LOADING...</span>
                </div>
            ) : (
                <div className={styles.movies}>
                    {movies.map((movie) => (
                        <Movie
                            key={movie.id}
                            id={movie.id}
                            title={movie.title}
                            year={movie.year}
                            summary={movie.summary}
                            genres={movie.genres}
                            coverImage={movie.medium_cover_image}
                        />
                    ))}
                </div>
            )}
        </div>
    );
}

export default Home;
  1. Details.js
import { useEffect } from "react";
import { useParams } from "react-router-dom";

function Details() {
    const { id } = useParams();
    const getMovie = async () => {
        const json = await (
            await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
        ).json();
        console.log(json);
    };
    useEffect(() => {
        getMovie();
    }, []);

    return <h1>MOVIE DETAILS</h1>;
}

export default Details;
  1. Movie.js
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import styles from "./Movie.module.css";

function Movie({ id, title, year, summary, genres, coverImage }) {
    return (
        <div className={styles.movie}>
            <img className={styles.movie_image} src={coverImage} alt={title} />
            <div>
                <h2 className={styles.movie_title}>
                    <Link to={`/movies/${id}`}>{title}</Link>
                </h2>
                <h3 className={styles.movie_year}>{year}</h3>
                <p className={styles.movie_summary}>{summary.length > 235 ? `${summary.slice(0, 235)}...` : summary}</p>
                <ul className={styles.movie_genres}>
                    {genres.map((genre) => (
                        <li key={genre}>{genre}</li>
                    ))}
                </ul>
            </div>
        </div>
    );
}

Movie.propTypes = {
    id: PropTypes.number.isRequired,
    title: PropTypes.string.isRequired,
    summary: PropTypes.string.isRequired,
    genres: PropTypes.arrayOf(PropTypes.string).isRequired,
    coverImage: PropTypes.string.isRequired,
};

export default Movie;
  1. styles.css
* {
    box-sizing: border-box;
}

body {
    margin: 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    background-color: #eff3f7;
    height: 100%;
}
  1. Home.module.css
.container {
    height: 100%;
    display: flex;
    justify-content: center;
}

.loader {
    height: 100vh;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: 300;
}

.movies {
    display: grid;
    grid-template-columns: repeat(2, minmax(400px, 1fr));
    grid-gap: 100px;
    padding: 50px;
    width: 80%;
    padding-top: 70px;
}

@media screen and (max-width: 1090px) {
    .movies {
        grid-template-columns: 1fr;
        width: 100%;
    }
}
  1. Movie.module.css
.movie {
    background-color: white;
    margin-bottom: 70px;
    font-weight: 300;
    padding: 20px;
    border-radius: 5px;
    color: #adaeb9;
    display: grid;
    grid-template-columns: minmax(150px, 1fr) 2fr;
    grid-gap: 20px;
    text-decoration: none;
    color: inherit;
    box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25),
        0 8px 16px -8px rgba(0, 0, 0, 0.3), 0 -6px 16px -6px rgba(0, 0, 0, 0.025);
}

.movie_image {
    position: relative;
    top: -50px;
    max-width: 150px;
    width: 100%;
    margin-right: 30px;
    box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25),
        0 18px 36px -18px rgba(0, 0, 0, 0.3), 0 -12px 36px -8px rgba(0, 0, 0, 0.025);
}

.movie_title a {
    margin-bottom: 5px;
    font-size: 24px;
    color: #2c2c2c;
    text-decoration: none;
}

.movie_genres {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    margin: 5px 0px;
    font-weight: 500;
}

.movie_summary {
    font-weight: 300;
}

.movie_genres li,
.movie_year {
    margin-right: 10px;
    font-size: 14px;
}

0개의 댓글