[포스코 x 코딩온] 웹 풀스택 12주차 회고 -2

sima·2023년 9월 26일
1

KDT web-8

목록 보기
14/17
post-thumbnail

Event Handling

React에서의 event

function Event() {

    const handleClick = () => {
        alert('클릭했습니다.');
    }

    const handleClick2 = (e, str) => {
        console.log(e);
        alert(str);
    }


    return <>
        <button onClick={handleClick}>클릭</button>
        <button onClick={(e) => handleClick2(e, '클릭2 클릭')}>클릭2</button>
    </>
}

export default Event;

React에서 event속성을 줄 때는 camelCase 사용, JSX 사용해 이벤트 핸들러 전달
함수에 인자를 담아 보낼땐 화살표 함수 사용

클래스 컴포넌트에서의 이벤트

import {Component} from "react";

class EventClass extends Component {

    constructor(props) {
        super(props);

        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        console.log(this);
        alert('클래스형 컴포넌트');
    }

    handleClick2 =() => {
        console.log('화살표함수', this);
        alert('클래스형 컴포넌트 2번');
    }
    
    handleClick3() {
    	alert('클래스형 컴포넌트 3번');
    };


    render() {
        return <>
            <button onClick={this.handleClick}>클릭class</button>
            <button onClick={this.handleClick2}>클릭class2번</button>
      		<button onClick={this.handleClick3.bind(this)}>클릭class3번</button>
        </>
    }

}

export default EventClass;

클래스 컴포넌트에서는 this 사용하여 이벤트 함수 접근
클래스 내에서 이벤트 함수 정의
일반 함수로 정의했을 경우, 생성자에 bind 사용 필수
화살표 함수로 정의했을 경우, 따로 생성자에 정의할 필요 없음(자동 바인딩)
render 내부에서 바로 바인딩할 수 있으나, 좋은 방법은 아님!

state, setState

state

React에서 유동적인 데이터 다루기 위한 개체, 계속 변하는 특정 상태(컴포넌트가 가지고 있는 값)
state가 변경될 시 자동 재렌더링(변수는 변경되도 렌더링되지 않음)
setState 메서드를 통해 상태를 변경할 수 있음

특징

클래스형 컴포넌트에서만 사용가능
this.state가 아닌 setState로만 값 변경
setState는 비동기적 동작함으로 이전 상태 기반으로 함수형태의 setState 사용

예시 코드

import {Component} from "react";

class Counter extends Component {

    state = {
        number : 0,
    }
    constructor(props) {
        super(props);
       
        this.handleIncrement = this.handleIncrement.bind(this);
    }

    handleIncrement() {
        this.setState((prevState) => ({ number : prevState.number + 1 }));
    }

    handleDecrement = () => {
        this.setState((prevState) => ({ number : prevState.number - 1 }));
    }


    render() {
        return (
            <div>
                <h1>{this.state.number}</h1>
                <button onClick={this.handleIncrement}>증가</button>
                <button onClick={this.handleDecrement}>감소</button>
            </div>
        )
    }
}

export default Counter;

클래스 컴포넌트 내부에 state란 이름, 객체형식으로 선언
버튼 클릭시, 바인딩된 이벤트 함수 호출
this.setState 사용하여 state값 변경

this.setState((prevState)=> ({ number : prevState.number + 1}));

state.number의 값을 prevState(이전 상태)값을 기준으로 +1
렌더링 후 태그에서 state값 접근할 때는 this.state 사용

React Hook

클래스 컴포넌트에서만 사용 가능했던 statelifeCycle을 함수형 컴포넌트에서도 사용하도록 돕는 기능

Hook 사용 규칙

  • 최상위 단계에서만 호출 가능
  • 반복문, 조건문, 중첩 함수 내부에서 절대 호출 불가
  • 오로지 함수형 컴포넌트 내부에서만 호출 가능
  • 커스텀 훅 이름은 use로 시작(권장사항)

useState()

사용 예시

import {useState} from "react";

export default function CounterFunc() {
 const [number, setNumber] = useState(0);

 const handleIncrement = () => {
     setNumber(()=> number + 1);
 }

 const handleDecrement =() => {
     setNumber(number - 1);
 }
 return <div>
   <h1>{number}</h1>
     <button onClick={handleIncrement}>증가</button>
     <button onClick={handleDecrement}>감소</button>
 </div>
};

useState()의 인자는 state의 초기값(값의 형태는 자유)
useState() 함수의 반환값은 배열을 반환, 첫번째 배열값은 현재 state, 두번째 배열값은 state를 바꿔주는 setter 함수
setNumber(()=> number + 1) - setter 함수 사용해 state값 변경

클래스형 컴포넌트 LifeCycle

LifeCycle

모든 React 컴포넌트에 존재하는 생명주기
Mount - DOM이 생성되고 웹브라우저 상에 나타났을 때

Update - props나 state가 바뀌었을 때
Unmount - 컴포넌트가 화면에서 제거될 때

예시 코드

import {Component} from "react";

class MyComponent extends Component {
    state = {
        childNumber : 0,
    }
    //생성될 때
    componentDidMount() {
        console.log('Mount');
    }

    //업데이트 될 때
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log('Update');
        console.log('prevProps ', prevProps);
        console.log('prevState ', prevState);
        console.log('snapshot ', snapshot);

    }
    //삭제될 때, 자식 컴포넌트의 state값은 초기화됨
    componentWillUnmount() {
        console.log('Unmount');
    }

    changeChildNumberState = () => {
        this.setState({ childNumber : this.state.childNumber + 1 });
    }
    render() {
        return <>
            <div>My Component {this.props.number}</div>
            <button onClick={this.changeChildNumberState}>PLUS Child</button>
        </>
    }
}

class LifeCycleClass extends Component {
    state = {
        number : 0,
        visible : true,
    };

    changeNumberState = () => {
        this.setState({ number : this.state.number + 1} );
    }

    changeVisibleState = () => {
        this.setState({ visible : !this.state.visible });
    }
    render() {
        return <>
            <button onClick={this.changeNumberState}>PLUS</button>
            <button onClick={this.changeVisibleState}>ON/OFF</button>
            {this.state.visible && <MyComponent number={this.state.number}/>}
        </>
    }
}

export default LifeCycleClass;

useEffect

컴포넌트가 처음 마운트/리렌더링/변수의 값이 변경될 때 특정 작업들을 실행할 수 있도록 하는 Hook

예시 코드

import {useState, useEffect} from "react";

function MyComponent({number}) {
    const [text, setText] = useState('');
    useEffect(()=> {
        console.log('항상 실행됩니다.');
    })

    useEffect(()=> {
        console.log('생성될때 실행됩니다');

        return ()=> {
            console.log('제거될때 실행됩니다');
        }
    }, []);

    useEffect(()=> {
        console.log('state가 변경될 때 실행됩니다');
    }, [text]);
    return <>
        <div>My Component {number}</div>
        <input type="text" value={text} onClick={(e)=> setText(e.target.value)}/>
    </>
}
export default function LifeCycleFunc() {
    const [number ,setNumber] = useState(0);
    const [visible ,setVisible] = useState(true);

    const changeNumberState = () => {
        setNumber(() => number + 1);
    }

    const changeVisibleState = () => {
        setVisible(() => !visible);
    }

    return (
        <>
        <button onClick={changeNumberState}>PLUS</button>
        <button onClick={changeVisibleState}>ON/OFF</button>
            {visible && <MyComponent number={number}></MyComponent>}
    </>
    )
}

useEffect(()=> , ) - 항상 실행
useEffect(()=>, []) - 최초 컴포넌트가 생성될 때 실행
useEffect(()=> return.. ,[]) - 제거될 때 실행
useEffect(()=>, [state]) - state 값이 변경될 때 실행


Ref, useRef

컴포넌트 내부에서 HTML 태그에 접근 가능하게 하는 기능
클래스형 컴포넌트에서만 사용이 가능
함수형 컴포넌트에서는 useRef()로 사용 가능

클래스형 컴포넌트 예시 코드

import React from "react";

class RefSample extends React.Component {
    myInput = React.createRef();
    handleFocus = () => {
        this.myInput.current.focus();
    };

    render() {
        return (
            <>
                <p>(클래스형 컴포넌트) 버튼 클릭시 input에 focus 처리</p>
                <input ref={this.myInput}/>
                <button onClick={this.handleFocus}>Focus</button>
            </>
        )
    }
}

export default RefSample;

React.createRef()로 클래스 내에 필드에 선언 후 태그에 ref this를 붙여 값으로 넣어 사용
Reference를 사용할 때는 this.current로 사용

함수형 컴포넌트 예시 코드

import {useRef} from "react";

const UseRef2 = () => {
    const idRef = useRef(1);

    const plusIdRef =() => {
        idRef.current += 1;
        console.log(idRef.current);
    }
    return (
        <>
            <p>useRef 로컬변수 사용</p>
            <h2>{idRef.current}</h2>
            <button onClick={plusIdRef}>plus ref</button>
        </>
    )

}

export default UseRef2;

useRef()로 함수 내 변수에 선언 후 ref에 값으로 넣어 사용
ref.current로 접근 가능
컴포넌트가 렌더링되어도 값이 그대로 유지되는 속성을 이용해 로컬변수로도 사용 가능


useMemo

함수형 컴포넌트 안에서, 렌더링 될 때 특정 값이 바뀌었을 때만 연산을 실행
렌더링될 때 마다 컴포넌트가 호출될 때 매번 내부 정의된 함수가 실행되는 경우를 막고, 결과값을 메모리에 저장 후 재사용

예시 코드

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

export default function UseMemo() {
    const [count, setCount] = useState(0);
    const [inputValue, setInputValue] = useState('');

    const calc = useMemo(()=> {
        console.log('calc...');
        for(let i=0 ; i<100000 ; i++) {
            return count * 2;
        }
    }, [count]);

  return (
      <>
          <input value={inputValue} onChange={(e)=> setInputValue(e.target.value)}/>
          <button onClick={()=> setCount(count + 1)}>Increment Count</button>
          <p>Count : {count}</p>
          <p>Dobuled Count : {calc}</p>
      </>
  )
};

useMemo()의 두번째 인자값인 의존 배열에서 지정한 state가 바뀌는 경우에만 콜백함수를 실행


useCallback

useMemo와 비슷하지만, useCallback은 컴포넌트 내 함수를 새로 만들지 않고 재사용하여 사용

예시 코드

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

export default function UseCallback() {
    const [text, setText] = useState('');

    const onChangeText = (e) => {
        setText(e.target.value);
    }

    const onChangeTextUseCallback = useCallback((e)=> {
        setText(e.target.value);
    }, []);


    return (
        <>
            <h1>useCallback Test</h1>
            <input onChange={onChangeText}/>
            <div>
                <span>작성한 값 : {text || '없음'}</span>
            </div>
        </>
    )
}

반복해서 생성되는 이벤트 핸들러 함수를 useCallback으로 감싸주면 함수를 메모이제이션해 컴포넌트가 리렌더링 되더라도 두번째 인자인 의존배열에 할당된 state가 바뀌지 않는 한 기존 함수를 계속 반환


useReducer

현재 state와 업데이트를 위해 필요한 정보를 담은 액션 dispatch를 전달받아 새로운 상태를 반환하는 함수, useState의 기능을 대체할 수 있음

예시 코드

import { useReducer } from "react";

const initialState = { count : 0 };
function reducer(state, action) {
    switch(action.type) {
        case 'INCREMENT': return { count : state.count + 1 };
        case 'DECREMENT': return { count : state.count - 1 };
        default : throw new Error('invalid action type');
    }
}
export default function UseReducer() {
    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <>
            <p>Count : {state.count}</p>
            <button onClick={() => { dispatch({ type : 'INCREMENT' }); }}></button>
            <button onClick={() => { dispatch({ type : 'DECREMENT' }); }}></button>
        </>
    )
}

useReducer()의 첫번째 인자값은 액션에 따라 작동하는 함수, 두번째 인자는 초기 state값을 가짐
컴포넌트 내부에 [state, dispatch] 를 가지는 reducer를 선언 후 dispatch() 로 업데이트 액션 값을 지정
useReducer()는 업데이트 함수를 컴포넌트 외부에서 가져와 사용할 수 있는 이점이 있음
객체, 배열과 같이 하위에 접근할 요소가 많은 경우 useReducer()를 이용하는게 좋음

1개의 댓글

comment-user-thumbnail
2023년 9월 27일

머시써용

답글 달기