본문 바로가기

React/소플 React 강의

[2022.07.29]React- 소플 강의 정리2

소플의 처음 만난 리액트 -초청 강의 수강

 

Hook

-훅은 무조건 최상위레벨에서만 호출해야 한다.

-컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 한다.

-리액트 함수 컴포넌트에서만(+우리가 직접만든 커스텀내에서도) 훅을 호출해야 한다.

-훅이 많아지면 헷갈리기에 상단에 훅을 호출하는 것을 모아 놓아 구분하는 것이 좋음

플러그인 추천

eslint-plugin-react-hooks

의존성배열이 잘못되면 알려줌

 

Custom Hook 만들기

-이름이 use로 시작하고 내부에서 다른 훅을 호출하는 하나의 자바스크립트 함수

-재사용성을 높이기 위한 목적으로 사용

 

▼ 아래와 같이 두가지 예시가 중복되는 코드가 많을 때 사용한다.

▼ 위 코드에서 커스텀 훅을 만든 코드

 

Custom Hook 사용하기

  위 코드에서 훅을 사용한 코드

 

*useCounter() 커스텀 훅 예제

chapter_07

 Accommodate.jsx
import React,{useEffect,useState} from "react";
import useCounter from "./useCounter";

const MAX_CAPACITY=10;

function Accommodate(props){
    const [isFull,setIsFull]=useState(false);
    const [count, increaseCount,decreaseCount]=useCounter(0);

    useEffect(()=>{
        console.log('===============');
        console.log('useEffect() is called.');
        console.log(`isFull:${isFull}`);
    })

    useEffect(()=>{
        setIsFull(count>= MAX_CAPACITY);
        console.log(`Current count value: ${count}`);
    },[count]);

    return(
        <div style={{padding:16}}>
           <div style={{ padding: 16 }}>
            <p>{`총 ${count}명 수용했습니다.`}</p>

            <button onClick={increaseCount} disabled={isFull}>
                입장
            </button>
            <button onClick={decreaseCount}>퇴장</button>

            {isFull && <p style={{ color: "red" }}>정원이 가득찼습니다.</p>}
          </div>
        </div>
    )
}

export default Accommodate;

 

useCounter.jsx
import {useState} from 'react';

function useCounter(initialValue){
    const [count,setCount]=useState(initialValue);

    const increaseCount=()=>setCount((count)=>count+1);//안전하려면 그냥 count+1말고 앞에 (count)=>count+1이렇게 앞에 붙여주는게 좋음
    const decreaseCount=()=>setCount((count)=>Math.max(count-1,0));

    return [count,increaseCount,decreaseCount];
}

export default useCounter;

 


React Event

 

이벤트 리스너(event listener)

이벤트 리스너란 이벤트가 발생했을 때 그 처리를 담당하는 함수를 가리키며, 이벤트 핸들러(event handler)라고도 한다.

onclick, textinput많이 사용

 

 

파라미터

 

* 클릭이벤트 처리하기 예제 -교재 p.261 

chapter_08> ConfirmButton.jsx
import React, { useState } from "react";

function ConfirmButton(props) {
    const [isConfirmed, setIsConfirmed] = useState(false);

    const handleConfirm = () => {
        setIsConfirmed((prevIsConfirmed) => !prevIsConfirmed);
    };

    return (
        <button onClick={handleConfirm} disabled={isConfirmed}>
            {isConfirmed ? "확인됨" : "확인하기"}
        </button>
    );
}

export default ConfirmButton;

 


condition rendering

조건에 따른 렌더링

condition=조건

 

 

자바스크립트의 Truthy와 falsy

 

엘리먼트 변수 Element Variables

리액트 엘리먼트를 변수처럼 다루는 방법

렌더링해야할 컴포넌트를 변수처럼 다루고 싶을 때 사용

 

 

인라인 조건 inline condition

조건문을 코드 안에 집어 넣는 것

많이 사용

 

 

inline If-Else

?연산자를 사용 (삼항연산자)

condition?true:false

많이 사용

작성참고

많이 하는 실수, 위처럼 말고 아래처럼 코드 쓰면 됨

 

로그인 여부를 나타내는 툴바 -교재 p.279

inline-if, inline else

chapter_09

Toolbar.jsx

import React from "react";

const styles = {
    wrapper: {
        padding: 16,
        display: "flex",
        flexDirection: "row",
        borderBottom: "1px solid grey",
    },
    greeting: {
        marginRight: 8,
    },
};

function Toolbar(props) {
    const { isLoggedIn, onClickLogin, onClickLogout } = props;

    return (
        <div style={styles.wrapper}>
            {isLoggedIn && <span style={styles.greeting}>환영합니다!</span>}

            {isLoggedIn ? (
                <button onClick={onClickLogout}>로그아웃</button>
            ) : (
                <button onClick={onClickLogin}>로그인</button>
            )}
        </div>
    );
}

export default Toolbar;

 

LandingPage.jsx

import React, { useState } from "react";
import Toolbar from "./Toolbar";

function LandingPage(props) {
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    const onClickLogin = () => {
        setIsLoggedIn(true);
    };

    const onClickLogout = () => {
        setIsLoggedIn(false);
    };

    return (
        <div>
            <Toolbar
                isLoggedIn={isLoggedIn}
                onClickLogin={onClickLogin}
                onClickLogout={onClickLogout}
            />
            <div style={{ padding: 16 }}>소플과 함께하는 리액트 공부!</div>
        </div>
    );
}

export default LandingPage;

 

▼디버깅할때 잠깐 랜더링 막아 놓을 때 이렇게 사용하면 좋음(일단 보여주는거 막으라 할때 임시적으로 가려놓고 나중에다시 보여서 배포하라하면 지우면 됨)

 

▼ 실행화면

클릭시 바뀐 화면

 

 


출석부 출력하기 예제 -교재 p.299

chapter_10

AttendanceBook.jsx

import React from "react";

const students = [
    {
        id: 1,
        name: "Inje",
    },
    {
        id: 2,
        name: "Steve",
    },
    {
        id: 3,
        name: "Bill",
    },
    {
        id: 4,
        name: "Jeff",
    },
];

function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student, index) => {
                return <li key={student.id}>{student.name}</li>;
            })}
            {/* {students.map((student, index) => {
                return <li key={`student.id-${student.id}`}>{student.name}</li>;
            })} */}
        </ul>
    );
}

export default AttendanceBook;

 


사용자 정보 입력받기 예제 -교재 p.324

chapter_11

SignUp.jsx

import React, { useState } from "react";

function SignUp(props) {
    const [name, setName] = useState("");
    const [gender, setGender] = useState("남자");

    const handleChangeName = (event) => {
        setName(event.target.value);
    };

    const handleChangeGender = (event) => {
        setGender(event.target.value);
    };

    const handleSubmit = (event) => {
        alert(`이름: ${name}, 성별: ${gender}`);
        event.preventDefault();
    };

    return (
        <form onSubmit={handleSubmit}>
            <label>
                이름:
                <input type="text" value={name} onChange={handleChangeName} />
            </label>
            <br />
            <label>
                성별:
                <select value={gender} onChange={handleChangeGender}>
                    <option value="남자">남자</option>
                    <option value="여자">여자</option>
                </select>
            </label>
            <button type="submit">제출</button>
        </form>
    );
}

export default SignUp;

 


섭씨 온도와 화씨온도 표시하기 예제- p.346

chapter_12

Calculator.jsx

import React, { useState } from "react";
import TemperatureInput from "./TemperatureInput";

function BoilingVerdict(props) {
    if (props.celsius >= 100) {
        return <p>물이 끓습니다.</p>;
    }
    return <p>물이 끓지 않습니다.</p>;
}

function toCelsius(fahrenheit) {
    return ((fahrenheit - 32) * 5) / 9;
}

function toFahrenheit(celsius) {
    return (celsius * 9) / 5 + 32;
}

function tryConvert(temperature, convert) {
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
        return "";
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
}

function Calculator(props) {
    const [temperature, setTemperature] = useState("");
    const [scale, setScale] = useState("c");

    const handleCelsiusChange = (temperature) => {
        setTemperature(temperature);
        setScale("c");
    };

    const handleFahrenheitChange = (temperature) => {
        setTemperature(temperature);
        setScale("f");
    };

    const celsius =
        scale === "f" ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit =
        scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
        <div>
            <TemperatureInput
                scale="c"
                temperature={celsius}
                onTemperatureChange={handleCelsiusChange}
            />
            <TemperatureInput
                scale="f"
                temperature={fahrenheit}
                onTemperatureChange={handleFahrenheitChange}
            />
            <BoilingVerdict celsius={parseFloat(celsius)} />
        </div>
    );
}

export default Calculator;

 

TemperatureInput.jsx

const scaleNames = {
    c: "섭씨",
    f: "화씨",
};

function TemperatureInput(props) {
    const handleChange = (event) => {
        props.onTemperatureChange(event.target.value);
    };

    return (
        <fieldset>
            <legend>
                온도를 입력해주세요(단위:{scaleNames[props.scale]}):
            </legend>
            <input value={props.temperature} onChange={handleChange} />
        </fieldset>
    );
}

export default TemperatureInput;

Card컴포넌트 만들기 예제 -p.369

chapter_13

Card.jsx

function Card(props) {
    const { title, backgroundColor, children } = props;

    return (
        <div
            style={{
                margin: 8,
                padding: 8,
                borderRadius: 8,
                boxShadow: "0px 0px 4px grey",
                backgroundColor: backgroundColor || "white",
            }}
        >
            {title && <h1>{title}</h1>}
            {children}
        </div>
    );
}

export default Card;

 

ProfileCard.jsx

import Card from "./Card";

function ProfileCard(props) {
    return (
        <Card title="Nana" backgroundColor="#4ea04e">
            <p>안녕하세요, 나나입니다.</p>
            <p>저는 리액트를 사용해서 개발하고 있습니다.</p>
        </Card>
    );
}

export default ProfileCard;

컨텍스트 사용해 테마변경 기능만들기 예제 -p.397

chpater_14

DarkOrLight.jsx

import { useState, useCallback } from "react";
import ThemeContext from "./ThemeContext";
import MainContent from "./MainContent";

function DarkOrLight(props) {
    const [theme, setTheme] = useState("light");

    const toggleTheme = useCallback(() => {
        if (theme == "light") {
            setTheme("dark");
        } else if (theme == "dark") {
            setTheme("light");
        }
    }, [theme]);

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            <MainContent />
        </ThemeContext.Provider>
    );
}

export default DarkOrLight;

 

MainContent.jsx

import { useContext } from "react";
import ThemeContext from "./ThemeContext";

function MainContent(props) {
    const { theme, toggleTheme } = useContext(ThemeContext);

    return (
        <div
            style={{
                width: "100vw",
                height: "100vh",
                padding: "1.5rem",
                backgroundColor: theme == "light" ? "white" : "black",
                color: theme == "light" ? "black" : "white",
            }}
        >
            <p>안녕하세요, 테마 변경이 가능한 웹사이트 입니다.</p>
            <button onClick={toggleTheme}>테마 변경</button>
        </div>
    );
}

export default MainContent;

 

ThemeContext.jsx

import React from "react";

const ThemeContext = React.createContext();
ThemeContext.displayName = "ThemeContext";

export default ThemeContext;

스타일컴포넌트 사용해 스타일링 예제 -p.436

chapter_15

Blocks.jsx

import styled from "styled-components";

const Wrapper = styled.div`
    padding: 1rem;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    justify-content: flex-start;
    background-color: lightgrey;
`;

const Block = styled.div`
    padding: ${(props) => props.padding};
    border: 1px solid black;
    border-radius: 1rem;
    background-color: ${(props) => props.backgroundColor};
    color: white;
    font-size: 2rem;
    font-weight: bold;
    text-align: center;
`;

const blockItems = [
    {
        label: "1",
        padding: "1rem",
        backgroundColor: "red",
    },
    {
        label: "2",
        padding: "3rem",
        backgroundColor: "green",
    },
    {
        label: "3",
        padding: "2rem",
        backgroundColor: "blue",
    },
];

function Blocks(props) {
    return (
        <Wrapper>
            {blockItems.map((blockItem) => {
                return (
                    <Block
                        padding={blockItem.padding}
                        backgroundColor={blockItem.backgroundColor}
                    >
                        {blockItem.label}
                    </Block>
                );
            })}
        </Wrapper>
    );
}

export default Blocks;

'React > 소플 React 강의' 카테고리의 다른 글

[2022.07.30]소플 강의 정리 3  (0) 2022.07.30
[2022.07.28]React- 소플 강의 정리  (0) 2022.07.28