본문 바로가기
취미 공부/Daily

Daily Coding - FE 4주차 - 47강 48강 49강

by Breadbread2 2024. 10. 20.
반응형

 

47강

모달과 같은 Overlay 컴포넌트는 전체 HTML에서 동작하게 해야한다.

컴포넌트의 위치를 옮겨줄 때 React Portal를 사용한다.

 

모달을 만들 때 자식 컴포넌트에 만드는 경우가 있는데 이때 모달의 요소를 바꾸기엔 엮에 있는 부분이 많으므로,

portal를 활용하여 전체 HTML으로 이동해준다.

ErrorModal.js

import React from 'react';
import ReactDOM from "react-dom";
import Card from './Card';
import Button from './Button';
import classes from './ErrorModal.module.css';

const Backdrop = (props) => {
  return (
    <div className={classes.backdrop} onClick={props.onConfirm} />
  )
}

const ModalOverlay = (props) => {
  return (
    <Card className={classes.modal}>
        <header className={classes.header}>
          <h2>{props.title}</h2>
        </header>
        <div className={classes.content}>
          <p>{props.message}</p>
        </div>
        <footer className={classes.actions}>
          <Button onClick={props.onConfirm}>Okay</Button>
        </footer>
      </Card>
  )
}

const ErrorModal = (props) => {
  return (
    <div>
      {ReactDOM.createPortal(
        <Backdrop onConfirm = {props.onConfirm} />,
        document.getElementById('backdrop-root')
      )}
      {ReactDOM.createPortal(
        <ModalOverlay 
          title={props.title}
          message={props.message}
          onConfirm={props.onConfirm}/>,
        document.getElementById('overlay-root')
      )}
        
        </div>
  );
};

export default ErrorModal;

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="backdrop-root"></div>
    <div id="overlay-root"></div>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

 

 

48강

Ref

=> Reference의 줄임말로 리액트의 dom을 참조한다

=> 상태를 사용하지 않고 DOM의 값들을 조작할 수 있다

=> DOM을 직접 참조하게 해준다

AddUser.js

import React, { useRef, useState } from 'react';

import Card from '../UI/Card';
import Button from '../UI/Button';
import ErrorModal from '../UI/ErrorModal';
import Wrapper from '../Helpers/Wrapper';
import classes from './AddUser.module.css';

const AddUser = (props) => {
  const [enteredUsername, setEnteredUsername] = useState('');
  const [enteredAge, setEnteredAge] = useState('');
  const [error, setError] = useState();

  const nameRef = useRef();
  const ageRef = useRef();

  const addUserHandler = (event) => {
    event.preventDefault();
    
    const enteredName = nameRef.current.value;
    const enteredAge = ageRef.current.value;

    console.log(nameRef.current.value);
    console.log(ageRef.current.value);

    if (enteredName.trim().length === 0 || enteredAge.trim().length === 0) {
      setError({
        title: '유효하지 않은 입력값',
        message: '유효한 나이와 이름을 입력해주세요 (빈 값이 아닌).',
      });
      return;
    }
    if (+enteredAge < 1) {
      setError({
        title: '유효하지 않은 나이',
        message: '유효한 나이를 입력해주세요 (> 0).',
      });
      return;
    }
    props.onAddUser (enteredName,enteredAge);
    nameRef.current.value = '';
    ageRef.current.value = '';

  };

  const usernameChangeHandler = (event) => {
    nameRef(nameRef.current.value);
  };

  const ageChangeHandler = (event) => {
    ageRef(ageRef.current.value);
  };

  const errorHandler = () => {
    setError(null);
  };

  return (
    <Wrapper>
      {error && (
        <ErrorModal
          title={error.title}
          message={error.message}
          onConfirm={errorHandler}
        />
      )}
      <Card className={classes.input}>
        <form onSubmit={addUserHandler}>
          <label htmlFor="username">사용자 이름</label>
          <input
            id="username"
            type="text"
            ref={nameRef}

          />
          <label htmlFor="age">나이</label>
          <input
            id="age"
            type="number"
            ref={ageRef}
          />
          <Button type="submit">사용자 추가</Button>
        </form>
      </Card>
    </Wrapper>
  );
};

export default AddUser;

 

 

초기화

    const enteredName = nameRef.current.value;
    const enteredAge = ageRef.current.value;

 

    nameRef.current.value = '';
    ageRef.current.value = '';

 

enteredName = '';

enteredAge = '';

=> const 는 변할 수 없는 값이므로 위 구문처럼 사용하면 안됨 그렇다고 let을 쓰면 가능하냐 이것도 안됨 그 이유는,

enteredName과 enteredAge에 빈 문자열을 할당해도 그 값은 단순히 변수의 값을 변경하는 것일 뿐, nameRef.current.value나 ageRef.current.value의 값 자체는 변경되지 않음. 즉, useRef로 참조하는 실제 입력 필드의 값을 빈 문자열로 바꾸려면 해당 참조 값을 직접 수정해야 함.

 

49강

사이드 이펙트란

useEffect()

 

리액트 구동 방식

1. UI 랜더링

2. 사용자 Input에 반응

3. 리랜더링

 

검색 창 자동완성 기능

1. 'ㄱ' 입력

2. onChange 이베트 리스터 & 핸들러  <-> HTTP Request <-> 서버

3. 리랜더링

 

사이트 이펙트

1. 키원드 값이 바뀔 때 -> 리랜더링

2. 서버에서 키워드로 자동완성 된 데이터를 받아왔을 때 -> 리랜더링

 

useEffect (( ) => {...}, [dependencies]);

 

() => {... } 

매 순간마다 dependecies가 바뀔 때 마다, 이 함수가 실행이 된다.

 

[dependencies]

첫 번째 인자 함수를 매번 실행을 시켜줘야하는 의존하는 값을 배열로 넣는다.

 

App.js

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

import Login from './components/Login/Login';
import Home from './components/Home/Home';
import MainHeader from './components/MainHeader/MainHeader';

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

  useEffect(() => {
    console.log('hello useEffect');
    const storedUserLoggedInInfo = localStorage.getItem('isLoggedIn');
    // 로그인이 되었다면 'isLoggedIn = true'
    if (storedUserLoggedInInfo === '1') setIsLoggedIn(true);
    // 로그아웃이 되었다면 ' isLoggedIn = false'
    else setIsLoggedIn(false);
  }, [])

  const loginHandler = (email, password) => {
    localStorage.setItem('isLoggedIn', '1')
    setIsLoggedIn(true);
  };

  const logoutHandler = () => {
    localStorage.removeItem('isLoggedIn')
    setIsLoggedIn(false);
  };

  return (
    <React.Fragment>
      <MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
      <main>
        {!isLoggedIn && <Login onLogin={loginHandler} />}
        {isLoggedIn && <Home onLogout={logoutHandler} />}
      </main>
    </React.Fragment>
  );
}

export default App;
반응형