[패스트캠퍼스] 프론트엔드 강의 학습후기 7주차

양정현·2022년 10월 17일
0

패스트캠퍼스

목록 보기
8/8

Props

  • 컴포넌트에 전달되는 단일 객체 (Children...)
  • 순수 함수처럼 동작함
    Props 를 수정하면 안됨
  • 어떤 상태를 바꾸려면 state 활용
  • 합성 : 여러 컴포넌트를 모아서 합성 가능
  • 추출 : 여러 컴포넌트를 따로 빼놓고 재사용 가능
import React from 'react'

function formatDate(date) {
  return date.toLocaleDateString();
}

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
      <img className="Avatar"
             src={props.author.avatarUrl}
             alt={props.author.name} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

const comment = {
  date: new Date(),
  text: 'I hope you enjoy learning React!',
  author: {
    name: 'Hello Kitty',
    avatarUrl: 'http://placekitten.com/g/64/64'
  }
};


export default function Extraction() {
  return (
    <Comment
    date={comment.date}
    text={comment.text}
    author={comment.author} />
  )
}

State

  • 컴포넌트 내의 상태, 자신의 출력값을 변경
  • Class component : state LifeCycle
// rcc : 리액트 컴포넌트 자동완성
import React, { Component } from 'react'

export default class ClassComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
    // state 기본 값 설정
  }

  componentDidMount() { // 그려지자마자 호출되는 아이
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() { // 이 컴포넌트가 사라지기 직전에 호출될 아이
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() { // return 한 값 자체를 render
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
  • function component : 훅으로 관리
import React, { useState, useEffect } from 'react';

export default function FunctionalComponent(){
  const [date, setDate] = useState(new Date());

  const tick = () => {
    setDate(new Date());
  }

  useEffect(() => {
    const interval = setInterval(() => tick(), 1000);

    return() => {
      clearInterval(interval);
    }
  }, []);

    return (
        <div>
          <h1>Hello, world!</h1>
          <h2>It is {date.toLocaleTimeString()}.</h2>
        </div>
    )
  
}
  • 직접 수정하면 비동기적일 수 있음
  • setState에서 첫 인자로 오는 state 쓰기

컴포넌트 생명주기

  • constructor: state 초기화, 메서드 바인딩
  • componentDidMount: Dom 노드 초기화 및 데이터 fetch
  • componentWillUnmonet: 타이머 제거 및 요청 취소 및 구독 해제
  • functional Component: hook으로 대부분 구현 가능

이벤트

  • 합성이벤트 : 인터페이스는 같지만 직접 대응되지 않음
    • 버블링 : 어떤 element를 클릭했을 때 부모로 전파
    • 캡쳐링 : 부모 요소가 자식 요소들 중 어떤 element에 클릭이 되었는지 보는 것
    • 캡쳐링 후 버블링이 일어남
import React, { Component } from 'react'

export default class Event extends Component {
  render() {
    const handleClick = () => {
      console.log('handleButtonClick')
    } 

    const handleMouseLeave = () => {
      console.log('handleMouseLeave')
    }

    const handleClickCapture = () => {
      console.log("handleClickCapture")
    }

    const handleClickCapture2 = () => {
      console.log("handleClickCapture2")
    }

    const handleClickBubble = () => {
      console.log("handleClickBubble")
    }

    return (
      <div onClickCapture={handleClickCapture}>
        <div onClickCapture={handleClickCapture2} onClick={handleClickBubble}>
          <button onClick={handleClick} onMouseLeave={handleMouseLeave}>button</button>
        </div>
      </div>
    
    )
  }
}

/*
!! console.log 

handleClickCapture
handleClickCapture2
handleButtonClick
handleClickBubble 
 */

조건부 렌더링

  • 어떤 상황일 때 ~ 를 render한다
  • if : if(condition){return A} eles {return B}
  • && : condition && A // fasly 주의!
  • 삼항연산자 : condition ? A : B
  • return null : 아예 안그리고 싶은 경우
import React from 'react'

function UserGreeting(props){
  return <h1>{props.name}, Welcome It's {props.count} times</h1>
}

function GuestGreeting(props){
  return <h1>Please sign up.</h1>
}

function Greeting(props){
  if(props.isLoggedIn){
    return <UserGreeting name="jimy" count={0}/>
  }
  return <GuestGreeting />;

}
export default function Condition() {
  const isLoggedIn = false;
  return (
    <div>
      <Greeting isLoggedIn={isLoggedIn}/>
    </div>
  )
}

list

  • (= array, 배열)
  • props 로 key를 꼭 넣어야 한다
    item 자체에 key를 주는 것이 아님
    key는 형제 사이에서만 고유한 값이어야 하고 같은 컴포넌트 사이 부모가 다른 아이들이면 같은 id를 써도 무방하다
  • map : 배열의 개별 요소를 순회함
  • default key : 키를 안주면 index를 알아서 key로 쓰고 있음
    (워닝은 나지만 동작은 함: 유효한 값을 줘야하는 이유, index는 수정될 수 있는 값)
  • 고유성 : 형제 사이에서만 고유하면 됨
  • key props : 키는 props 로 넘어가지 않는다
import React from 'react'

export default function List() {
  // const numbers = [1, 2, 3, 4, 5]
  // return (
  //   <ul>
  //     {numbers.map((item) => (<li>{item}</li>))}
  //   </ul>
  // );

  const todos = [
    {id: 1, text: 'drink water'},
    {id: 2, text: 'wash car'},
    {id: 3, text: 'Listen Lecture'},
    {id: 4, text: 'ge to bed'}
  ];

  const Item = (props) => {
    return (
      <li>{props.id}
      {props.text}</li>
    );
  };

  const todoList = todos.map((todo) => <Item key={todo.id} id={todo.id} text={todo.text}/>);
  
  return <>{todoList}</>;
}

Form

  • 제어 컴포넌트 : 컴포넌트 자체에 value 를 주는 것
  • controlled component : input의 value를 state로 관리
import React, { useState } from 'react'

export default function ControlledComponent() {
  const [name, setName] = useState('');
  const [essay, setEssay] = useState("please ~")

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

  function handleEssayChange(event){
    setEssay(event.target.value);
  };
  function handleSubmit(event) {
    alert(`name: ${name}, essay: ${essay}`);
    event.preventDefault();
  }
  return (

    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={name} onChange={handleChange}/>
      </label>
      <label>
        Essay:
        <textarea value={essay} onChange={handleEssayChange}/>
      </label>
      <input type="submit" value="submit"/>
    </form>
  )
}
  • 다중 입력 : 하나의 핸들러를 가지고 target의 name으로 할 수 있음
  • uncontrolled component : 폼 자체의 내부 상태를 활용
import React, {useRef} from 'react'

export default function UncontrolledComponent() {
  const fileInputRef = useRef(null);

  function handleSubmit(event){
    event.preventDefault();
    alert(`selected file = ${fileInputRef.current.files[0].name}`);
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <label>
          Upload file:
          <input type="file" ref={fileInputRef}/>
        </label>
        <br/>
        <button type="submit">submit</button>
      </form>
    </div>
  )
}
  • defaultValue, ref : 기본값, value 확인

Hook

  • Class의 단점 보완, 재사용성 강화
  • class를 사용하지 않고도 state와 다른 기능들을 사용할 수 있음
  • 사용 규칙
    • 최상위에서만 hook을 호출해야 한다
      함수지만 반복문, 조건문, 중첩 함수에서 hook을 사용하면 에러가 난다
      원하지 않는 side effect가 발생할 수 있음
    • React 함수 컴포넌트 내에서만 hook 호출 해야 함
  • effect가 업데이트 시마다 실행되는 이유?
    • 표현을 하나로 합치고, 버그를 방지하기 위함
    • 가독성이 좋아짐
  • Reducer
import React , {useReducer} from 'react'

export default function Reducer() {
  const initalState = {count: 0};

  function reducer(state, action){
    switch(action.type){
      case 'reset':
        return initalState;
        case 'increment':
        return {count: state.count + 1};
        case 'decrement':
        return {count: state.count - 1};
      default: 
        throw new Error();
    }
  }
  const [state, dispatch] = useReducer(reducer, initalState);
  
  return (
    <div>
      Count : {state.count}

      <button onClick={() => dispatch({type: 'reset'})}>Reset</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </div>
  )
}
  • state
import React , {useState} from 'react'

export default function State() {
  const initialCount = 0;
  const [count, setCount] = useState(initialCount);
  
  return (
    <div>
      Count : {count}

      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prev => prev - 1)}>-</button>
      <button onClick={() => setCount(prev => prev + 1)}>+</button>
    </div>
  )
}

Composition

  • 컴포넌트 안에서 컴포넌트를 모아 출력한 것
  • 상속 대신 합성을 사용하여 컴포넌트 간에 코드를 재사용함
  • 담는 방법
    • children
    • custom
  • typeof 를 통해 확장 가능 (다양한 상황을 품을 수 있음)

HOC

  • 컴포넌트 로직을 재사용하기 위한 고급기술
  • React API의 일부가 아님
  • 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수
    ( 함수를 받아서 함수를 리턴 )

Memoization

  • 동일한 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술
  • React.memo : 동일한 props로 rendering을 한다면 react.memo를 사용하여 성능 향상을 할 수 있음
    memo를 사용하면 컴포넌트를 랜더링 하지 않고 마지막으로 랜더링 한 것을 재사용한다.
import React, { Profiler, memo, useState, useMemo } from 'react'
import "./CommentItem.css";

function CommentItem({title, content, likes, onClick}) {
  const [clickCount, setClickCount] = useState("");

  function onRenderCallback(
    id, // 방금 커밋된 Profiler 트리의 "id"
    phase, // "mount" (트리가 방금 마운트가 된 경우) 혹은 "update"(트리가 리렌더링된 경우)
    actualDuration, // 커밋된 업데이트를 렌더링하는데 걸린 시간
    baseDuration, // 메모이제이션 없이 하위 트리 전체를 렌더링하는데 걸리는 예상시간 
    startTime, // React가 언제 해당 업데이트를 렌더링하기 시작했는지
    commitTime, // React가 해당 업데이트를 언제 커밋했는지
    interactions // 이 업데이트에 해당하는 상호작용들의 집합
  ) {
    // 렌더링 타이밍을 집합하거나 로그...
    console.log(`actualDuration(${title}: ${actualDuration})`)
  }

  const handleClick = () => {
    onClick();
    setClickCount(prev => prev + 1); // rate check를 다시 쓰게 됨, 이때 스는게 useMemp
    alert(`${title} 눌림`);
  }

  const rate = useMemo(() => {
    console.log("rate check");
    return likes > 10 ? 'good' : "bad"; // useMemo 추가하면 rate check 안함
  }, [likes]);

  return (
    <Profiler id="CommentItem" onRender={onRenderCallback}>
    <div className="CommentItem" onClick={handleClick}>
      <span>{title}</span>
      <br/>
      <span>{content}</span>
      <br/>
      <span>{likes}</span>
      <br/>
      <span>{rate}</span>
      <br/>
      <span>{clickCount}</span>
    </div>
    </Profiler>
  )
}

export default memo(CommentItem);
/*
  memo를 쓰지 않으면 
  console.log(`actualDuration(${title}: ${actualDuration})`) 에서 확인 시
  새로 생기는 것들을 1부터 ~ 새로생긴 것만큼 계속 생김

  memo를 쓰면
  추가 된 것들만 console 에 찍힘
  : 비효율을 줄일 수 있다

  부모에 onClick 을 넣으면 memo를 써도 안쓴것처럼 됨..
  comments 자체가 reRender 되기 때문
  (설정한 함수가 계속 실행되기 때문)
  >> useCallback 사용
*/

  • Profiler API를 이용하여 리액트 성능 분석
  • useCallback : 주입되거나 전달될 때 메모를 해놨음에도 계속 바뀜
    (내가 받은 props가 바뀌기 때문)
import React, { useCallback } from 'react'
import CommentItem from './CommentItem'

export default function Comments({commentList}) {
  const handleClick = useCallback(() => {
    console.log("눌림");
  }, []);

  return (
    <div>
      {commentList.map(comment => <CommentItem
        key={comment.title}
        title={comment.title}
        content={comment.content}
        likes={comment.likes}
        onClick={handleClick}
      />)}
    </div>
  )
}

0개의 댓글