오늘은
오늘은 일요일입니다!! 주말에도 쉬지않고 프로그래밍 공부를...!!
재밌다가도 어렵다가도 알것같다가도 모르겠는..!!
혼자 프로그래밍에게 밀당을 당하고 있습니다~~~
React 공부
<<컴포넌트 트리에 데이터 공급>>
<Props Drilling>
* Props Drilling 문제
<App/>
onCreate() diaryList + onRemove(), onEdit() => 그냥 거쳐가기만 하는 Prop들이 존재함
<DiaryEditor/> <DiaryList/>
onRemove(), onEdit()
<DiaryItem/>
=> DiaryList component는 diaryList, onRemove(), onEdit() 세개의 props를 받지만
onRemove()와 onEdit() props는 자신이 사용하지 않는 props이다
=> 전달만 하는 component가 중간에 많이 생기게 될 경우
props의 이름을 바꾸기도 어려워지고, 코드 작성과 수정에 악영향을 끼칠 수 있다
= 이러한 현상을 Props Drilling 이라고 한다
=> 단방향 데이터 흐름을 지키는 React를 사용하다보면 발생하는 문제임
* Props Drilling 문제 해결
<App/>
모든 데이터
<Provider/> <Counter/>
on create() diaryList onRemove() onEdit()
l l l
<DiaryEdito/> <DiaryList/> l => Context=문맥
l
<DiaryItem/> --------
=> 모든 데이터를 가지고 있는 component가 Provider 라는 공급자 역할을 하는 자식 component에게
자신이 가지고 있는 모든 데이터를 다 준다
-> 공급자 Provider component는 자신의 자손에 해당하는 모든 component들에게
직통으로 데이터를 줄 수 있다
=> Context(문맥)
= Provider component의 자식 node, 자식 component들로 배치되어
해당 Provider component가 공급하는 모든 데이터에 접근할 수 있는
이런 component들의 영역을 Context, 문맥이라고 한다
* Counter component 처럼
Provider component의 자손으로 배치되지 않은 component는
Provider compenent가 공급하는 데이터에 접근할 수 없으며,
같은 문맥, Context가 아니라고 할 수 있다
<Context 문맥>
* Context 생성
const MyContext = React.createContext(defaultValue);
* Context Provider를 통한 데이터 공급
<MyContext.Provider value={전역으로 전달하고자 하는 값}>
{ /* context 안에 위치할 자식 컴포넌트들 */ }
</MyContext.Provideer>
* export, import
import React, {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
createContext,
} from "react";
=> React를 import할 때 = 그냥 React로 import
= 이름을 바꿔서 import 받을 수 있다
=> useCallback, useEffect 등과 같이 부가적 기능들은 {}중괄호 비구조화 할당을 통해 import
= import 시 이름을 바꿀 수 없다
=> "react" 파일에서 default로 import를 받을 수 있는 것은 export default가 된 그 요소만 가능
export const 로 그냥 export 된 요소들은 그 이름을 비구조화 할당을 통해서만 import를 받을 수 있다
* App.js
import React, {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
createContext,
} from "react";
// data state를 전역적으로 공급할 수 있도록 도와줄
// DiaryStateContext 생성
// 다른 component들이 Context에 접근을 해서
// 사용하고 싶은 data를 받아갈 수 있도록 export를 해주어야 한다
export const DiaryStateContext = createContext(null);
// export default는 파일 하나당 하나밖에 쓸 수 없기 때문에
// 그냥 export만 해준다
// 1. 공급자 = Context가 가지고 있는 Provider component를 사용
const App = () => {
return (
// 2. App component가 return하고 있는 부분의 최상위 태그를 바꿔준다
// = 공급자 component로 wrapping
// 3. value라는 prop으로 data, 공급을 내려주어야 한다
// = Provider component에 내려준 값은 언제든지 가져다 쓸 수 있는 값이다
<DiaryStateContext.Provider value={data}>
<div className="App">
<DiaryEditor />
<div>전체 일기 : {data.length}</div>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}%</div>
<DiaryList />
</div>
</DiaryStateContext.Provider>
);
};
* Provider Componet에 전달하는 value에 data 뿐만 아니라
모든 함수 component를 전달해주면 될 것 같지만,
Provider 도 결국 component이기 때문에 prop이 바뀌어 버리면 재생성이 된다
=> Provider component가 재생성되면 그 밑에 있는 component들도 강제로 재생성된다
= data state가 바뀔때마다 re-render가 되어 만들어 두었던 최적화가 다 풀리게 된다
=> 이러한 경우 문맥 Context를 중첩으로 사용해주면 된다
Context를 새로 하나 더 생성해준다
파일에서 Context를 생성하는 데에는 한계가 없기 때문에 많이 생성해도 된다
export const DiaryDispatchContext = createContext(null);
-> DiaryStateContext.Provider의 자식 요소로 추가해준다
return (
<DiaryStateContext.Provider value={data}>
<DiaryDispatchContext.Provider value={memoizedDispatch}>
<div className="App">
<DiaryEditor />
<div>전체 일기 : {data.length}</div>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}%</div>
<DiaryList />
</div>
</DiaryDispatchContext.Provider>
</DiaryStateContext.Provider>
);
=> <DiaryStateContext.Provider value={data}> 위에 있는
component Provider는 state Context Provider가 되고
=> <DiaryDispatchContext.Provider value={memoizedDispatch}> 아래에 있는
component Provider는 dispatch Context Provider가 된다
=> onCreate, onEdit과 onRemove는 dispatch Context Provider에게 value로 전달을 해주면 된다
onCreate, onEdit과 onRemove는 재생성되지 않는 함수들로 이루어져 있기 때문에
dispatch Context Provider는 re-rendering 되지 않는다
=> 전달해 주어야 할 함수들을 하나의 값으로 묶어서 prop으로 전달
// useMemo()를 활용하여 묶는다
const memoizedDispatch = useMemo(() => {
return { onCreate, onRemove, onEdit };
}, []); // 재생성 되는 일이 없도록 deps를 빈 배열로 전달한다
=> useMemo를 활용하는 이유
const dispatches = {
onCreate, onRemove, onEdit
}
= useMemo를 활용하지 않고 그냥 묶어주게 되면
App component가 재생성 될 때 dispatches 객체도 재생성이 되기 때문이다
= 최적화가 풀리지 않도록 방지해준다
* DiaryEditor
import { DiaryDispatchContext } from "./App";
// onCreate prop은 더이상 받지 않는다
// const DiaryEditor = React.memo(({ onCreate }) => {
const DiaryEditor = React.memo(() => {
// 대신 DiaryDispatchContext에서 비구조화 할당으로 가져온다
const { onCreate } = useContext(DiaryDispatchContext);
* DiaryList
import DiaryItem from "./DiaryItem";
import { useContext } from "react";
import { DiaryStateContext } from "./App";
// diaryList = App component의 data state값
// context에서 공급을 받으면 되기 때문에
// App component의 data 값을 prop으로 받을 필요가 없어짐
// DiaryDispatchContext 에서 onEdit, onRemove를 가져오므로 prop으로 받아 올 필요X
// const DiaryList = ({ onEdit, onRemove, diaryList }) => {
const DiaryList = () => {
// useContext라는 hook을 사용하여 diaryList를 꺼내온다
// DiaryStateContext를 인자로 받아온다 (값을 꺼내오고 싶은 Context를 인자로 받음)
// Chrome의 React Developer Tools의 Components tap에서
// hooks 부분의 Context가 값을 받아오는 것을 확인할 수 있다
const diaryList = useContext(DiaryStateContext);
return (
<div className="DiaryList">
<h2>일기 리스트</h2>
<h4>{diaryList.length}개의 일기가 있습니다.</h4>
<div>
{diaryList.map((it) => (
// DiaryItem에게 전달하는 props drilling도 제거를 해준다
// <DiaryItem key={it.id} {...it} onEdit={onEdit} onRemove={onRemove} />
<DiaryItem key={it.id} {...it} />
))}
</div>
</div>
);
};
DiaryList.defaultProps = {
diaryList: [],
};
export default DiaryList;
* DiaryItem
import { memo, useContext, /*useEffect,*/ useRef, useState } from "react";
import { DiaryDispatchContext } from "./App";
// onRemove와 onEdit을 더 이상 prop으로 받지 않는다
const DiaryItem = ({
// onRemove,
// onEdit,
id,
author,
content,
emotion,
created_date,
}) => {
// useEffect(() => {
// console.log(`${id}번 일기아이템 렌더`);
// });
// DiartDispatchContext로 부터 onRemove와 onEditd을 받아온다
const { onRemove, onEdit } = useContext(DiaryDispatchContext);
Question
React를 공부하다보니 새삼 어렵다는 것을 느끼며
궁금했던 것들을 모아 정리를 한번 해보았습니다
* 비구조화 할당
비구조화 할당(destructuring assignment)은 객체나 배열에서 데이터를 추출하여
변수에 할당하는 JavaScript의 문법 중 하나입니다.
이를 통해 객체나 배열의 속성이나 요소를 개별 변수로 분해해서 할당할 수 있습니다.
ex)
const person = { name: 'John', age: 30, country: 'USA' };
// 기존 방식
const name = person.name;
const age = person.age;
// 비구조화 할당
const { name, age } = person;
* 중첩 Context
부모 component의 prop이 바뀌면 재생성 되면서
자식들도 모두 강제로 재생성
Context의 자식으로 Context를 만들면
부모 Context의 Provider가 재생성 될 때 자식 Context Provider는 재생성되지 않는건가..?
=>
React Context에서는 부모 Context Provider의 value가 변경되더라도
자식 Context Provider가 재생성되지 않습니다.
이는 React에서 성능 최적화를 위해 특별히 처리되는 부분 중 하나입니다
* deps
deps = dependency array를 줄여서 "deps"라고 부르는 경우가 있습니다.
* dispatch 함수
React에서 dispatch 함수는 주로 컴포넌트에서
상태를 변경하거나 액션을 처리할 때 사용됩니다.
useState 훅을 사용하여 컴포넌트의 상태를 관리합니다.
* React Hooks
React Hooks는 React 함수형 컴포넌트에서
상태(state)와 생명주기(lifecycle) 기능을 사용할 수 있게 해주는 기능입니다.
클래스형 컴포넌트의 기능을 함수형 컴포넌트에서도 사용할 수 있도록 도와주며,
코드를 간결하게 만들고 재사용성을 높일 수 있게 해줍니다.
주요한 Hooks에는 useState, useEffect, useContext, useReducer 등이 있습니다.
이들은 각각 상태를 다루는데, 생명주기와 관련된 작업을 수행하는데,
컨텍스트를 사용하는데 도움을 주는 등 다양한 기능을 제공합니다.
Hooks는 함수형 컴포넌트에서 사용되며,
각각의 Hook은 특정한 목적을 가지고 있어서 필요한 Hook을 선택적으로 사용할 수 있습니다.
Hooks는 React의 함수형 프로그래밍 모델을 강화하고,
클래스형 컴포넌트와 비교했을 때 더 간단하고 직관적인 코드를 작성할 수 있도록 돕습니다.
* feat. ChatGPT........
2023. 01. 14 (일)
프로그래머 도전기 104일차 (0) | 2024.01.18 |
---|---|
프로그래머 도전기 103일차 (0) | 2024.01.15 |
프로그래머 도전기 101일차 (0) | 2024.01.13 |
프로그래머 도전기 100일차 (2) | 2024.01.11 |
프로그래머 도전기 99일차 (2) | 2024.01.10 |