리액트 상태관리, Hooks 학습

이기훈·2020년 12월 30일
0

Input 상태 관리

import React, { useState } from 'react';

const InputSample = () => {
    const [input, setInput] = useState({
        name: '',
        nickname:''
    });
    const {name, nickname} = input;
    const onChange = (e) => {
        const {value, name} = e.target;
        setInput({
            ...input,
            [name]: value
        })
    };
    return (
        <>
            <input name="name" placeholder="이름" onChange={onChange} value={name} />
            <input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
        </>
    )
}

리액트 Key Problem

  • 리액트에서 배열을 렌더링 할 때에는 key 라는 props 를 설정해야한다.
  • key 값은 각 원소들마다 가지고 있는 고유값으로 설정
 {users.map(user => (
        <User user={user} key={user.id} />
 ))}

배열에서 원소 추가, 삭제, 수정

const [user, setUser] = useState([
    {
        id: 1,
        username: 'shmallow',
        email: 'shmallow@naver.com'
    },
    {
        id: 2,
        username: 'hypering',
        email: 'glesmallow@naver.com'
    }
]);

const nextId = useRef(4); 

// 추가
const onCreate = () => {
    const newUser = {
        id: nextId.current,
        username,
        email
    };
    setUser(user.concat(newUser));
    nextId.current += 1;
}

// 삭제
const onRemove = (id) => {
    setUser(user.filter(user => user.id !== id));
}

// 수정
const onToggle = (id) => {
    setUser(user.map(user => user.id === id ? {...user, active: !user.active}));
}

useEffect를 사용

  • 컴포넌트가 마운트 -> 처음 나타났을 때,
  • 컴포넌트가 언마운트 -> 사라질 때,
  • 업데이트될 때 -> 특정 props가 바뀔 때
useEffect(() => {
    console.log('컴포넌트가 화면에 나타남');
    return () => {
      console.log('컴포넌트가 화면에서 사라짐');
    };
 }, []);

useCallback 사용

  • 특정 함수를 새로 만들지 않고, 재사용하고 싶을 때 사용한다.

// 리렌더링 -> onCreate가 새로 만들어지는데, props가 바뀌지 않으면
// 리렌더링 하지 않고 재사용할 수 있다.
const onCreate = useCallback(() => {
    const newUser = {
        id: nextId.current,
        username,
        email
    }
    setUser(user.concat(newUser));
    nextId.current += 1;
}, [user, username, email]);

주의사항

  • 함수 안에서 사용하는 상태 혹은 props가 있다면, deps 배열안에 포함시켜야 한다.

  • 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없다.

useMemo 사용

  • 연산된 값을 useMemo Hook을 사용해 재사용하는 방법
const countActiveUsers = (users) => {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

const count = useMemo(() => countActiveUsers(user), [user])

React.memo 사용

  • 컴포넌트의 props가 바뀌지 않았다면, 리렌더링 방지해줄 수 있는 함수
const CreateUser = ({ username, email, onChange, onCreate }) => {
  return (
    <div>
      <input
        name="username"
        placeholder="계정명"
        onChange={onChange}
        value={username}
      />
      <input
        name="email"
        placeholder="이메일"
        onChange={onChange}
        value={email}
      />
      <button onClick={onCreate}>등록</button>
    </div>
  );
};
export default React.memo(CreateUser);

useReducer 사용

  • 상태 업데이트 로직을 분리하게 해주는 Hook
  • 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리할 수 있다.

useState 사용

import React, { useState } from 'react';

function Counter() {
  const [number, setNumber] = useState(0);

  const onIncrease = () => {
    setNumber(prevNumber => prevNumber + 1);
  };

  const onDecrease = () => {
    setNumber(prevNumber => prevNumber - 1);
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

useReducer 사용

import React, { useReducer } from 'react';

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

function Counter() {
  const [number, dispatch] = useReducer(reducer, 0);

  const onIncrease = () => {
    dispatch({ type: 'INCREMENT' });
  };

  const onDecrease = () => {
    dispatch({ type: 'DECREMENT' });
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

useReducer vs useState

  • 관리하는 값이 하나면 useState, 여러 개면 useReducer가 편하다.

Context API를 사용해 전역 값 관리

  • Props의 깊이가 깊어질수록 복잡한 구조를 가지는 문제가 발생!
  • Context API를 사용하면 프로젝트 안에서 전역적으로 사용할 수 있는 값을 관리할 수 있게 해준다.

context 생성

const UserDispatch = React.createContext(null);
  • Context 안에 Provider 컴포넌트를 이용해, Provider로 감싸진 어떤 컴포넌트 중 어디서든 우리가 Context의 값을 다른 곳에서 조회해서 사용할 수 있게 한다.

Context 사용


const reducer = (state, action) => {
  switch (action.type) {
    case 'CREATE_USER':
      return {
        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;
  }
}

export const UserDispatchContext = React.createContext(null);
export const UserStateContext = React.createContext(null);

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

<UserDispatchContext.Provider value={dispatch}>
    <UserStateContext.Provider value={state}>
        {children}
    </UserStateContext>
</UserDispatchContext>

다른 파일에서 사용

import { UserDispatchContext, UserStateContext } from './App';

불변성 관리

  • 리액트에서는 배열이나 객체를 업데이트 해야 할 때에는 직접 수정하면 안되고, 불변성을 지켜주면서 업데이트를 해줘야 한다.
  • Immer 모듈을 사용하면 더 쉽게 관리할 수 있다.
const object = {
  a: 1,
  b: 2
};

const nextObject = {
  ...object,
  b: 3
};

참고 사이트

다음 학습 예정

0개의 댓글