profile image

L o a d i n g . . .

벌써 74퍼!

 

contextAPI를 다룰것이다

 

 

 

 

 

전달만 하는 비효율적인 props (프롭스드릴링)의 문제점을 해결할 것이다

 

 

 

 

 

모든 데이터를 가진  component가 provider 컴포넌트에게 모든 데이터를 준다.

그리고 공급자인 provider 컴포넌트가 자신의 자손에 해당하는 모든 컴포넌트들에게 직접적으로 제공한다

그리고 provider의 자식 컴포넌트들은 이 provider컴포넌트에게 직접적으로 제공받는다

 

이렇게하면 코드의 가독성도 올라가고 프롭스 드릴링도 사라지게 된다.

 

 

 

 

 

 

 

 


Context; 문맥

 

이렇게 공급자 컴포넌트의 자식 노드; 자식 컴포넌트들로 해당 provider 컴포넌트가 공급하는 모든 데이터에 접근할 수 있는 컴포넌트들의 영역

 

 

 

 

 

 

 

 

 


Context생성과 Provider를 이용해 프롭스 드릴링 제거

 

 

 

 

일기 data state를 전역적으로 공급할 수 있도록 도와줄 컨텍스트 생성

const DiaryStateContext = React.createContext();

 

 

 

 

export const DiaryStateContext = React.createContext();

context를 내보내줘야 다른 컴포넌트들이 context에 접근해서 공급자가 공급하는 데이터를 받을 수 있기때문에 diaryStateContext앞에 export를 붙여준다.

 

 

 

 


 

export default 는 파일 하나 당 하나만 사용 가능

App 컴포넌트에서는 기본적으로 App컴포넌트를 내보내고 부가적으로는 DiaryStateContext를 내보낸다 고 이해

 

 

 

 

 

import React, { useRef, useEffect, useMemo, useCallback, useReducer } from 'react';

 

'react' 파일에서 React는 export default가 된 것이고 export const 인 중괄호 안에있는 애들은 비구조화 할당이 되었기 때문에 이름변경 불가.

 

 

 

 

 


Provider(공급자)를 만들어 데이터 공급하기

  return (
    <DiaryStateContext.Provider>
      <div className="App">

        <DiaryEditor onCreate={onCreate} />
        <div>전체 일기 : {data.length}</div>
        <div>기분 좋은 일기 개수 : {goodCount}</div>
        <div>기분 좋은 일기 개수 : {badCount}</div>
        <div>기분 좋은 일기 비율 : {goodRatio}</div>
        <DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
      </div>
    </DiaryStateContext.Provider>
  );

App리턴부분에 DiaryStateContext 컨텍스트가 가진 Provider 컴포넌트를 생성해주었다.

 

 

 

 

 

Context.Provider가 최상위에서 공급하고 있다는 것을 확인할 수 있다.

 

 

 

데이터 공급하기

context.Provider에 value 공급

123으로 숫자형 데이터를 전달했을때 

 react Developer tools에서 value 프롭이 123인걸 확인 할 수 있다

이때 내려받은 값은 언제든지 가져다 쓸 수 있는 값

 

 

 

이제 다이어리 데이터를 내려받아보자

  return (
    <DiaryStateContext.Provider value={data}>
      <div className="App">

        <DiaryEditor onCreate={onCreate} />
        <div>전체 일기 : {data.length}</div>
        <div>기분 좋은 일기 개수 : {goodCount}</div>
        <div>기분 좋은 일기 개수 : {badCount}</div>
        <div>기분 좋은 일기 비율 : {goodRatio}</div>
        <DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
      </div>
    </DiaryStateContext.Provider>
  );

value에 원하던 데이터가 들어온 것을 확인할 수 있다

 

 

 

const DiaryList = ({ onEdit, onRemove, diaryList }) => {
    return (
        <div className="DiaryList">
            <h2>일기 리스트</h2>
            <h4>{diaryList.length}개의 일기가 있습니다.</h4>
            <div>
                {diaryList.map((it) => (
                    <DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
                ))}
            </div>
        </div>
    );
};

app컴포넌트에서 prop으로 받던 diaryList 데이터 값을 useContext를 사용해보자 

 

 

 

 

 

 

 


useContext

 

 

useContext 훅 또한 react의 기능이기때문에 import받아야한다. 

인자로는 context를 전달하라고 툴팁에 적혀있다.

우리는 DiaryStateContext를 전달받을것이다.

 

 

import { useContext } from "react";
import { DiaryStateContext } from "./App";
import DiaryItem from "./DiaryItem";

const DiaryList = ({ onEdit, onRemove }) => {

    const diaryList = useContext(DiaryStateContext);
    return (
        <div className="DiaryList">
            <h2>일기 리스트</h2>
            <h4>{diaryList.length}개의 일기가 있습니다.</h4>
            <div>
                {diaryList.map((it) => (
                    <DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
                ))}
            </div>
        </div>
    );
};

DiaryList.defaultProps = {
    diaryList: []
}

export default DiaryList;

 

hooks의 Context기능을 통해 data가 들어온걸 확인할 수 있다.

 

 

 

 

 

그러나 사실 이전부터 data state는 프롭스 드릴링이 일어났지 않았다

프롭스 드릴링이 일어났던 

애들도 고쳐보자

 

 


Provider또한 결국 컴포넌트이기때문에

prop 이 바뀌면 재생성된다.

=== 밑에있는 컴포넌트들도 재생성된다.

 

=== onCreate onEdit onRemove 리랜더링이 된다

 

그동한 했던 최적화가 의미가 없어짐

 

 

문맥(Context) 중첩사용

export const DiaryDispatchContext = React.createContext();
  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext>
        <div className="App">
          <DiaryEditor onCreate={onCreate} />
          <div>전체 일기 : {data.length}</div>
          <div>기분 좋은 일기 개수 : {goodCount}</div>
          <div>기분 좋은 일기 개수 : {badCount}</div>
          <div>기분 좋은 일기 비율 : {goodRatio}</div>
          <DiaryList onEdit={onEdit} onRemove={onRemove} />
        </div>
      </DiaryDispatchContext>
    </DiaryStateContext.Provider>
  );

ContextProvider 안에 ContextProvider가 중첩이 된 것을 확인할 수 있다.

 

아래에 있는 Context.Priver는 onCreate onEdit onRemove 는 재생성되지 않는 함수들이기 때문에 리랜더링이 일어나지 않도록 할 것이다

 

  const memoizedDispatches = useMemo(() => {
    return { onCreate, onRemove, onEdit }
  }, []);

useMemo를 사용해 memoizedDispatches로 묶어주었다. memoizedDispatches는 재성생 될 일 없게 아무것도 없는 빈배열로 인자를 전달해주었다.

 

 

  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider value={memoizedDispatches}>
        <div className="App">
          <DiaryEditor />
          <div>전체 일기 : {data.length}</div>
          <div>기분 좋은 일기 개수 : {goodCount}</div>
          <div>기분 좋은 일기 개수 : {badCount}</div>
          <div>기분 좋은 일기 비율 : {goodRatio}</div>
          <DiaryList />
        </div>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );

 

app컴포넌트 리턴 하위 프로바이더의 value를 memoizedDispatches로 지정해주고 DiaryEditor 컴포넌트로 이동해서 onCreate 프롭스 받는 곳을 수정해준다.

 

 

 

 

 

 

기존 DiaryEditor에 props로 전달되던 onCreate 를 지워주고

 

 

const {onCreate} = useContext(DiaryDispatchContext);

비구조화 할당으로 onCreate를 가져온다.

 

 

 

이후 확인해보면

 

 

정상적으로 onCreate가 되는것을 확인 할 수 있다

 

 

 

이제 onEdit, onRemove도 프롭스 드릴링 제거

const DiaryList = ({ onEdit, onRemove }) => {
    const diaryList = useContext(DiaryStateContext);
    return (
        <div className="DiaryList">
            <h2>일기 리스트</h2>
            <h4>{diaryList.length}개의 일기가 있습니다.</h4>
            <div>
                {diaryList.map((it) => (
                    <DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
                ))}
            </div>
        </div>
    );
};

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓프롭스 드릴링 제거↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

const DiaryList = () => {
    const diaryList = useContext(DiaryStateContext);
    return (
        <div className="DiaryList">
            <h2>일기 리스트</h2>
            <h4>{diaryList.length}개의 일기가 있습니다.</h4>
            <div>
                {diaryList.map((it) => (
                    <DiaryItem key={it.id} {...it} />
                ))}
            </div>
        </div>
    );
};

 

 

DiaryItem으로 넘어가서 onEdit랑 onRemove 프롭스 제거

 

 

 

import React, { useContext, useRef, useState } from "react";
import { DiaryDispatchContext } from "./App";

const DiaryItem = ({
    author,
    content,
    created_date,
    emotion,
    id
}) => {

    const { onEdit, onRemove } = useContext(DiaryDispatchContext);
    const [isEdit, setIsEdit] = useState(false);
    const toggleIsEdit = () => setIsEdit(!isEdit);

    const [localContent, setLocalContent] = useState(content);
    const localContentInput = useRef();

    const handleRemove = () => {
        if (window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
            onRemove(id);
        };
    }

    const handleQuitEdit = () => {
        setIsEdit(false);
        setLocalContent(content);
    }

    const handleEdit = () => {
        if (localContent.length < 5) {
            alert("5자 이상 입력하세요");
            localContentInput.current.focus();
            return;
        }

        if (window.confirm(`${id}번 째 일기를 수정하시겠습니까?`)) {
            onEdit(id, localContent);
            toggleIsEdit();
        }
        onEdit(id, localContent);
    }


    return (
        <div className="DiaryItem">
            <div className="info">
                <span>
                    작성자 : {author} | 감정점수 : {emotion}
                </span>
                <br />
                <span className="date">
                    {new Date(created_date).toLocaleString()}
                </span>
            </div>
            <div className="content">
                {isEdit ? (
                    <>
                        <textarea
                            ref={localContentInput}
                            value={localContent}
                            onChange={(e) => { setLocalContent(e.target.value) }}>
                        </textarea>
                    </>
                ) : (
                    <>{content}</>
                )}
            </div>
            {isEdit ? (<>
                <button onClick={handleQuitEdit}> 수정 취소 </button>
                <button onClick={handleEdit} > 수정 완료 </button>
            </>) : (
                <>
                    <button onClick={toggleIsEdit}> 수정하기 </button>
                    <button onClick={handleRemove}> 삭제하기 </button>
                </>
            )}
        </div>
    );
};

export default React.memo(DiaryItem);

 

 

 

프롭스 드릴링 제거 후 수정, 삭제작동까지 확인 완료

 

 

 

 

 

 

 

 

 

 

 

 

 

참고 :

- 한 입 크기로 잘라먹는 리액트

 

반응형
복사했습니다!