React.js - 2

useEffect를 사용하여 마운트/언마운트/업데이트 시 할 작업 설정하기

-첫번째 파라미터에는 함수, 두번째 파라미터에는 의존값이 들어있는 배열(deps)을 넣음.

마운트/언마운트

마운트: 처음 나타났을 때
-props로 받은 값을 컴포넌트의 로컬 상태로 설정
-외부 API 요청
-라이브러리 사용
-setInterval를 이용한 반복작업 또는 setTimeout을 통한 작업 예약

언마운트: 사라질 때
-setInterval, setTimeout 이용하여 등록한 작업 clear하기
-라이브러리 인스턴스 제거

cleanup함수: useEffect에서 반환하는 함수. deps가 비어있는 경우 컴포넌트가 사라질 때 호출.

업데이트: 특정 props가 바뀔 때

deps에 특정 값 넣기

-컴포넌트가 처음 마운트 될 때도, 지정한 값이 바뀔 때에도 호출이 됨. 또한 언마운트시에도 호출이 되고, 값이 바뀌기 직전에도 호출이 됨.

deps 파라미터를 생략하기

-컴포넌트가 리렌더링 될 때마다 호출.(부모 컴포넌트가 리렌더링 되면 자식 컴포넌트도 리렌더링 됨.)

수정한 UserList.js 코드

import React, { useEffect } from 'react';

function User({ user, onRemove, onToggle }) {
  useEffect(() => {
    console.log(user);
  });
  return (
    <div>
      <b
        style={{
          cursor: 'pointer',
          color: user.active ? 'green' : 'black'
        }}
        onClick={() => onToggle(user.id)}
      >
        {user.username}
      </b>
      &nbsp;
      <span>({user.email})</span>
      <button onClick={() => onRemove(user.id)}>삭제</button>
    </div>
  );
}

function UserList({ users, onRemove, onToggle }) {
  return (
    <div>
      {users.map(user => (
        <User
          user={user}
          key={user.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
      ))}
    </div>
  );
}

export default UserList;

useMemo를 사용하여 연산한 값 재사용하기

-이전에 계산한 값을 재사용함으로써 불필요한 호출을 줄여 자원 낭비를 줄임.
-첫번째 파라미터에는 어떻게 연산할지 정의하는 함수를, 두번째 파라미터에는 dpes 배열을 넣어줌.

useCallback을 사용하여 함수 재사용하기

-특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용.
-함수 안에서 사용하는 상태 혹은 props가 있다면 (deps)배열 안에 포함시켜야 함. props로 받아온 함수가 있다면 또한 (deps)에 넣어주어야 함.

수정한 App.js 코드

import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });
  const { username, email } = inputs;
  const onChange = useCallback(
    e => {
      const { name, value } = e.target;
      setInputs({
        ...inputs,
        [name]: value
      });
    },
    [inputs]
  );
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }
  ]);

  const nextId = useRef(4);
  const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  }, [users, username, email]);

  const onRemove = useCallback(
    id => {
      setUsers(users.filter(user => user.id !== id));
    },
    [users]
  );
  const onToggle = useCallback(
    id => {
      setUsers(
        users.map(user =>
          user.id === id ? { ...user, active: !user.active } : user
        )
      );
    },
    [users]
  );
  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
}

export default App;

React.memo를 사용한 컴포넌트 리렌더링 방지

컴포넌트의 props가 바뀌지 않았다면 리렌더링을 방지하여 컴포넌트의 리렌더링 성능을 최적화 시켜줄 수 있음.

함수형 업데이트

콜백함수의 파라미터에서 최신 값을 참조할 수 있으므로 deps배열에 파라미터를 넣지 않아도 됨.

useReducer를 사용하여 상태 업데이트 로직 분리하기

useReducer 이해하기

-컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있음.
-reducer: 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해줌.

function reducer(state, action) {
  return nextState;
}

action은 주로 type값을 지닌 객체형태이지만 자유로움.

-useReducer

const [state, dispatch] = useReducer(reducer, initialState);

state는 우리가 앞으로 컴포넌트에서 사용할 수 있는 상태, dispatch는 액션을 발생시키는 함수

App컴포넌트를 useReducer로 구현하기

import React, { useRef, useReducer, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

const initialState = {
  inputs:{
    username: '',
    email: ''
  },
  users:[
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]',
      active: false
    }]
}
  function reducer(state, action){
    switch(action.type){
      case 'CHANGE_INPUT':
      return{
        ...state,
        inputs: {
          ...state.inputs,
          [action.name]: action.value
        }
    };
      case 'CREATE_USER':
        return{
          inputs: initialState.inputs,
          users: state.users.concat(action.user)
        };
      case 'TOGGLE_USER':
        return{
          ...state,
          users: state.users.map(user =>
            user.id === action.id ? { ...user, active: !user.active } : user
          )
        };
      case 'REMOVE_USER':
        return{
          ...state,
        users: state.users.filter(user => user.id !== action.id)
        }
      default:
        return state;
  }
}

  function App(){
    const[state, dispatch] = useReducer(reducer, initialState);
    const nextId = useRef(4);
    const {users} = state;
    const{username, email} = state.inputs;
    const onChange = useCallback(e => {
      const { name, value } = e.target;
      dispatch({
        type: 'CHANGE_INPUT',
        name,
        value
      });
    }, []);
    
    const onCreate = useCallback(() => {
      dispatch({
        type: 'CREATE_USER',
        user: {
          id: nextId.current,
          username,
          email
        }
      });
      nextId.current += 1;
    }, [username, email]);

    const onToggle = useCallback(id => {
      dispatch({
        type: 'TOGGLE_USER',
        id
      });
    }, []);
  
    const onRemove = useCallback(id => {
      dispatch({
        type: 'REMOVE_USER',
        id
      });
    }, []);

    const count = useMemo(() => countActiveUsers(users), [users]);
    return(
      <>
      <CreateUser username = {username} email = {email} onChange = {onChange} onCreate ={onCreate} />
      <UserList users={users} onToggle = {onToggle} onRemove = {onRemove} />
      <div>활성사용자수: {count} </div>
      </>
    );
  };

export default App;

커스텀 Hooks 만들기

-반복되는 로직을 쉽게 재사용.
-Hooks를 사용하여 원하는 기능을 구현해주고, 컴포넌트에서 사용하고 싶은 값들을 반환.

커스텀 hooks로 만들어준 useInput.js

import {useState, useCallback} from 'react';

function useInputs(initialForm){
  const[form, setForm] = useState(initialForm);
  const onChange = useCallback((e)=>{
        const {name, value} = e.target;
        setForm(form => ({...form, [name]: value}));
  },[]);
  const reset = useCallback(()=> setForm(initialForm), [initialForm]);
  return [form, onChange, reset];
};

export default useInputs;

Context API를 사용하여 전역값 관리

새로운 context 만들기

-React.createContext() 함수 사용

const UserDispatch = React.createContext(null);

-Context를 만들면, Context 안에 Provider 라는 컴포넌트가 들어있고, 이 컴포넌트를 통하여 Context 의 값을 정할 수 있음.

useState vs useReducer

useReducer를 사용하면 Context API를 사용해서 dispatch를 전역적으로 사용 할 수 있게 해주어 컴포넌트에게 함수를 전달해줘야 하는 상황에서 코드의 구조가 훨씬 깔끔해짐.

Immer를 사용한 더 쉬운 불변성 관리

-우리가 상태를 업데이트할 때 불변성을 신경쓰지 않으면서 업데이트해줄 수 있음.

import produce from 'immer';

produce라는 이름으로 immer를 불러옴. produce함수에 첫번째 파라미터로는 수정하고 싶은 상태, 두번째 파라미터에는 어떻게 업데이트하고 싶을지 정의하는 함수.

-함수형 업데이트를 사용하는 경우에는 더 편하게 코드를 작성할 수도 있는데, produce함수에서 첫번째 파라미터를 생략하고 업데이트 함수를 넣어주면 반환값은 상태를 업데이트해주는 함수가 됨.

클래스형 컴포넌트

-render()메서드: 렌더링 하고 싶은 JSX를 반환.
-props를 조회할 때는 this.props를 조회.

커스컴 메서드 만들기

-클래스 안에 메서드 선언.
-상태를 업데이트할 때는 this.setStat함수 사용.

메서드와 컴포넌트 인스턴스 사이의 관계가 끊기지 않도록 하는 방법

-클래스의 생성자 메서드 constructor에서 bind작업을 해주는 것
-커스텀 메서드를 선언 할 때 화살표 함수 문법을 사용
-onClick에서 새로운 함수를 만들어서 전달

상태 선언하기

state를 선언 할 때에는 constructor내부에서 this.state 를 설정, 무조건 객체 형태.

setState의 함수형 업데이트

setState 는 단순히 상태를 바꾸는 함수가 아니라 상태로 바꿔달라고 요청해주는 함수로 이해해야 함.

LifeCycle Method

-컴포넌트가 브라우저상에 나타나고, 업데이트되고, 사라지게 될 때, 에러가 났을 때 호출되는 메서드
-클래스형 컴포넌트에서만 사용할 수 있음.

마운트

마운트될 때 발생하는 생명주기
-constructor: 컴포넌트의 생성자 메서드. 가장 먼저 실행
-getDerivedStateFromProps: props로 받아온 것을 state에 넣어주고 싶을 때 사용. 앞에 static을 필요로 하고, 이 안에서는 this를 조회 할 수 없음
-render: 컴포넌트를 렌더링하는 메서드
-componentDidMount: 컴포넌트의 첫번째 렌더링이 마치고 나면 호출되는 메서드

업데이트

-getDerivedStateFromProps
-shouldComponentUpdate: 컴포넌트가 리렌더링 할지 말지를 결정하는 메서드
-render
-getSnapshotBeforeUpdate: 컴포넌트에 변화가 일어나기 직전의 DOM 상태를 가져와, 특정 값을 반환하면 그 다음 발생하게 되는 componentDidUpdate함수에서 받아와서 사용 가능.
-componentDidUpdate: 리렌더링을 마치고 화면에 우리가 원하는 변화가 모두 반영되고 난 뒤 호출되는 메서드

언마운트

-componentWillUnmount: 컴포넌트가 화면에서 사라지기 직전에 호출.

componentDidCatch

-에러 처리(props를 전달하지 않아 생기는 등)
-첫번째 파라미터에서 에러의 내용, 두번째 파라미터에서 에러가 발생한 위치를 알려줌.

좋은 웹페이지 즐겨찾기