본문 바로가기
Web Study

벨로퍼트 모던 리액트 정리 1

by 쿠리의일상 2023. 4. 3.

https://react.vlpt.us/basic/09-multiple-inputs.html

 

9. 여러개의 input 상태 관리하기 · GitBook

9. 여러개의 input 상태 관리하기 지난 튜토리얼에서 우리는 input 상태를 관리하는 방법에 대하여 알아보았는데요, 이번에는 input 이 여러개일때는 어떻게 관리해야 하는지 알아보겠습니다. 우선

react.vlpt.us

// 인풋 필드 여러개 다루기
import React, { useState } from 'react'

export default function PracticeInput() {
  const [input, setInput] = useState({
    name : '',
    nickname : '',
  });

  const {name, nickname} = input;

  const onChange = (e) => {
    const {name, value} = e.target;
    setInput({
      ...input,
      [name]:value,
    })
  }

  const onReset = () => {
    setInput({
      name : '',
      nickname : ''
    })
  }

  return (
    <div>
      <span>이름 : </span>
      <input 
        type="text"
        placeholder='이름'
        name='name'
        onChange={onChange}
        value={name}
      />
      <br/>
      <span>닉네임 : </span>
      <input 
        type="text"
        placeholder='닉네임'
        name='nickname'
        onChange={onChange}
        value={nickname}
      />
      <br/>
      <button onClick={onReset}>초기화</button>
    </div>
  )
}
불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고
이에 따라 필요한 리렌더링이 진행된다.

또한 컴포넌트 업데이터 성능 최적화를 제대로 할수 있기도 하다.

 

리액트에서 객체를 업데이트하게 될 때에는 기존 객체를 직접 수정하면 안되며

새로운 객체를 만들어서 새 객체 변화를 주어야 한다.

 

useRef

특정 DOM을 선택해야할 때 사용

 

함수형 컴포넌트에서 ref 를 사용하여 useRef() 함수를 사용한다.

클래스형 컴포넌트에서는 콜백 함수를 사용하거나 React.createRef() 를 사용한다.

  const nameInput = useRef('');
  const onReset = () => {
    setInput({
      name : '',
      nickname : ''
    });
    nameInput.current.focus();
  }
 <input 
        type="text"
        placeholder='이름'
        name='name'
        onChange={onChange}
        value={name}
        ref={nameInput}
      />

 

useRef 로 컴포넌트 안의 변수 관리하기

useRef 로 관리하는 변수는 값이 바뀐다고 하여 컴포넌트가 리렌더링 되지 않는다.

리액트 컴포넌트에서의 상태는 상태를 바꾸는 함수를 호출하고나서 그 다음 렌더링 이후 업데이트 된 상태를 조회할 수 있지만,

useRef 로 관리하고 있는 변수는 설정 후 바로 조회가 가능하다.

  • scroll 위치
  • 외부 라이브러리를 사용하여 생성된 인스턴스
  • setTimeout, setInterval 을 통하여 만들어진 id

등의 값을 관리할 수 있다.

 

useRef 를 사용할 때 파라미터 값을 넣어주면 이 값이 current 값의 기본값이 된다.

그리고 이 값을 수정하려고 한다면 .current 로 조회하거나 수정하면 된다.

 

 

 

map 함수 사용하여 컴포넌트 그리기

export default function PracticeArray() {
  const tmpArr = [
    {
      name : '강아지',
      email : 'dksdksa',
    },
    {
      name : '고양이',
      email : 'sa',
    },
    {
      name : '앵무새',
      email : 'qqqwera',
    }
  ]
  return (
    <div>
      {tmpArr.map((e, idx)=> {
        e['id'] = idx;
        return <PracticeArrayItem {...e} />
      })}
    </div>
  )
}

 

리액트에서의 배열 변화

배열에 변화를 줄 때에는 객체와 마찬가지로, 불변성을 지켜주어야 한다.
그렇기 때문에, 배열의 push, splice, sort 등의 함수를 사용하지 않아야한다.
만약에 사용해야 한다면, 기존의 배열을 한번 복사하고 나서 사용할 것

 

불변성을 지키면서 배열에 새 항목을 추가하는 방법에는

1. 전개 연산자 사용

export default function PracticeArray() {
  const [inputs, setInputs] = useState({
    username: '',
    email: '',
  })
  const [users, setUsers] = useState([
    {
      id:0,
      username : '강아지',
      email : 'dksdksa',
    },
    {
      id:1,
      username : '고양이',
      email : 'sa',
    },
    {
      id:2,
      username : '앵무새',
      email : 'qqqwera',
    }
  ]);
  
  const nextId = useRef(3);
  const onCreate = () => {
    const newUser ={
      id:nextId.current,
      username : username,
      email:email,
    }
    setUsers([
      ...users,
      newUser,
    ])

    setInputs({
      username :'',
      email:'',
    })
    nextId.current++;
  }
  const onChange = (e) => {
    const {name, value} = e.target;

    setInputs({
      ...inputs,
      [name] : value,
    })
  }
  const {username, email} = inputs;

  return (
    <div>
      <CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate}/>
      <UserList users={users} />
    </div>
  )
}

2. concat() 함수 사용

원래의 배열을 수정하지 않고 새로운 원소가 추가된 새로운 배열을 만들어준다.

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

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

 

 

삭제나 수정의 경우는 id 값을 활용할 것

  const onDelete = (id) => {
    setUsers(users.filter((e) => e.id !== id));
  }

특정 id 를 인자로 받아서 filter 메서드를 사용하여 삭제를 구현

  const onToggle = (id) => {
    setUsers(users.map((user) => 
      user.id === id 
      ? {...user, active: !user.active}
      : user
    ))
  }

 

 

Mount 시 하는 작업

  • props로 받은 값을 컴포넌트의 로컬 상태로 설정
  • 외부 API 요청 (REST API)
  • 라이브러리 사용
  • setInterval 을 통한 반복작업이나 setTimeout 을 통한 작업 예약

 

Unmount 시 하는 작업

  • setInterval, setTimeout 을 사용하여 등록한 작업들 clear 하기 - clearInterval, clearTimeout
  • 라이브러리 인스턴스 제거

useEffect() 의 deps 에 특정 값 넣기

deps 에 특정 값을 넣게 된다면,
1. 컴포넌트가 처음 마운트 될 때에도 호출이 되고,
2. 지정한 값이 바뀔 때에도 호출되고,
3. 언마운트시에도 호출이되고,
4. 값이 바뀌기 직전에도 호출이 된다.

useEffect 안에서 사용하는 상태나 props 가 있다면 deps 에 넣어주는 것이 규칙

만약 넣지 않는다면 useEffect 에 등록한 함수가 실행 될 때 최신 props나 상태를 가리키지 않게 된다.

 

deps 파라미터를 생략하면

컴포넌트가 리렌더링 될 때마다 호출된다.

리액트 컴포넌트는 부모 컴포넌트가 리렌더링 되면 자식 컴포넌트의 변경 사항이 없다고 해도 리렌더링 된다.

이는 Virtual DOM 에 해당되며, 실제 DOM에는 바뀐 내용이 있는 컴포넌트만 변화가 반영되는 것이다.

이는 최적화가 가능하여 기존의 내용을 그대로 사용하면서 Virtual DOM에 렌더링 하는 리소스를 아낄 수 있다.

 

 

useMemo

성능 최적화를 위하여 연산된 값을 저장하는 Hook 이다.

useMemo(콜백함수, deps 배열)

배열에 담긴 내용이 바뀌면 등록된 콜백 함수를 호출하여 연산 후, return 으로 들어오는 값을 useMemo 로 지정해준 변수에 저장해준다.

 

즉, 관련없는 state 나 props가 변경될 때, useEffect 가 호출할 필요가 없는 경우, 이를 막기 위해 사용해주는 것임

 

useMemo는 특정 결과값을 재사용할 때 사용한다.

이와 유사한 훅으로는 useCallback 이 존재하며, 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용해주는 훅이다.

 

기본적으로 컴포넌트가 리렌더링 될 때마다 선언해준 함수들도 재선언되므로 이를 방지하기 위해서

 const onCreate = useCallback(() => {
    const newUser ={
      id:nextId.current,
      username : username,
      email:email,
    }
    setUsers([
      ...users,
      newUser,
    ])

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

  const onDelete = useCallback((id) => {
    setUsers(users.filter((e) => e.id !== id));
  }, [users])

  const onToggle = useCallback((id) => {
    setUsers(users.map((user) => 
      user.id === id 
      ? {...user, active: !user.active}
      : user
    ))
  }, [users]);

함수의 선언부를 useCallback() 으로 감싸주면 된다.

 

다만 함수 안에서 사용하는 상태나 props 는 deps 배열 안에 포함시켜야한다.