본문 바로가기

React

[React 강의 노트(4)] 커스텀 Hook 만들기, context API, Immer

반응형

react강의 노트 | Byte Degree [4주차]

 

 

1. 커스텀 hook 만들어보기

 

useInputs

- useState, useCallback

 

import { useState, useCallback } from "react";

//커스텀 hook 만들어서 사용해보기
function UseInputs(inintialForm) {
  const [form, setForm] = useState(inintialForm);
  const onChange = useCallback((e) => {
    const { name, value } = e.target;
    setForm((form) => ({ ...form, [name]: value }));
  }, []);
  const reset = useCallback(() => setForm(inintialForm), [inintialForm]);

  return [form, onChange, reset];
}

export default UseInputs;

 

useInputs

- useReducer, useCallback

 

import { useCallback, useReducer } from "react";

//useReducer 사용해보기
function reducer(state, action) {
  switch (action.type) {
    case "CHANGE_USER":
      return {
        ...state,
        [action.name]: action.value,
      };
    case "RESET_INPUT":
      return Object.keys(state).reduce((acc, current) => {
        acc[current] = "";
        return acc;
      }, {});
    default:
      return state;
  }
}

function useInputs(inintialForm) {
  const [form, dispatch] = useReducer(reducer, inintialForm);

  const onChange = useCallback((e) => {
    const { name, value } = e.target;
    dispatch({
      type: "CHANGE_USER",
      name,
      value,
    });
  }, []);

  const reset = useCallback(() => {
    dispatch({
      type: "RESET_INPUT",
    });
  }, []);

  return [form, onChange, reset];
}

export default UseInputs;

 

 

 

2. context API - useContext를 사용하여 전역 값 관리하기

 

- useList를 보면 useList 컴포넌트는 onChange props를 직접 사용하지 않고  App.js에서 user 컴포넌트로 전달해주는 역할만 한다. 이러한 코드를 개선하기 위해서 context API 사용으로 App.js에서 바로 User 컴포넌트로 넘어가게 하기.

 

//useContext 사용을 안한다면
import React, from "react";

function Child({ text }) {
  return <div>안녕하세요 {text}</div>;
}

function Parent({ text }) {
  return <Child text={text} />;
}

function Grandparent({ text }) {
  return <Parent text={text} />;
}

function ContextSample() {
  return <Grandparent text="good" />;
}

export default ContextSample;
//props를 계속 넘겨줘야한다

 

 

//useContext hook 사용
import React, { createContext, useContext } from "react";

const MyContext = createContext("defaultValue");

function Child() {
  const text = useContext(MyContext);
  return <div>안녕하세요 {text}</div>;
}

function Parent() {
  return <Child />;
}

function Grandparent() {
  return <Parent />;
}

function ContextSample() {
  return (
    <MyContext.Provider value="good">
      <Grandparent />;
    </MyContext.Provider>
  );
}

export default ContextSample;

 

- props의 넘김 없이 바로 받을 수 있다.

 

- reducer을 사용하지 않고 useState로 내부에서 모든걸 작업했다면 dispatch가 없기 때문에 useContext 사용이 조금 복잡.

 

 

3. Immer 라이브러리 사용으로 쉽게 불변성 지키기

 

- 설치 : npm install immer

 

const Object = {
	a:1,
    b:2
};

Object.b = 3; //기존 객체가 변함

//불변성 지키기
const Object = {
	a:1,
    b:2
};

const nextObject = {
	...Object, //기존 객체 복사
    b:3
};

 

댓글을 추가하는 코드를 작성한다면

 

const state = {
  posts: [
    {
      id: 1,
      title: '제목입니다.',
      body: '내용입니다.',
      comments: [
        {
          id: 1,
          text: '와 정말 잘 읽었습니다.'
        }
        //이 밑으로 댓글을 추가하려면?
      ]
    },
    {
      id: 2,
      title: '제목입니다.',
      body: '내용입니다.',
      comments: [
        {
          id: 2,
          text: '또 다른 댓글 어쩌고 저쩌고'
        }
      ]
    }
  ],
  selectedId: 1
};

 

const nextState = {
  ...state, //기존 객체 가져오고
  posts: state.posts.map(post => //객체중 id=1 post 찾기
    post.id === 1 //id=1이면
      ? {
          ...post, //id=1인 post 가져오고
          comments: post.comments.concat({
            id: 3,
            text: '새로운 댓글' //새롭게 작성된 post concat으로 연결
          })
        }
      : post //id=1 아니면 기존 그냥 유지
  )
};

----------------↓---------------------
//immer 사용시
const nextState = produce(state, draft => {
  const post = draft.posts.find(post => post.id === 1);
  post.comments.push({
    id: 3,
    text: '와 정말 쉽다!'
  });
});

 

- immer 사용하면 불변성을 해치는 코드(ex) push)를 작성해도 대신 불변성 유지를 해준다.

 

 

- immer사용해보기

 

const state = {
  number: 1,
  dontChangeMe: 2
};

const nextState = produce(state, draft => {
  draft.number += 1;
});

console.log(nextState);
// { number: 2, dontChangeMe: 2 }

 

- produce(바꿔줄 객체or배열, 어떻게 바꿀지 함수(draft로 받아옴))

- push도 가능 

완전 좋은 라이브러리다....

 

- reducer immer로 구현하기

case "TOGGEL_USER":
  return {
    ...state,
    users: state.users.map((user) =>
      user.id === action.id ? { ...user, active: !user.active } : user
    ),
  };

//immer사용, TOGGEL_USER
case "TOGGEL_USER":
  return produce(state, (draft) => {
    const user = draft.users.find((user) => user.id === action.id);
    user.active = !user.active;
  });

 

- 데이터 구조가 복잡하거나 필요한 곳에서만 사용하는 것이 좋다.

- 간단하게 구현할 수 있는 것은 immer사용보다 native reducer사용하기