UI 구성하기
정찬욱
- 기본값은 size prop이 없거나
size={undefined}
로 전달될 때 사용됩니다. 그러나 만약size={null}
또는size={0}
으로 전달된다면, 기본값은 사용되지 않습니다.
- 조건을 테스트하기 위해, JavaScript는 왼쪽을 자동으로 불리언으로 변환합니다. 그러나 왼쪽이
0
이면 전체 표현식이 해당 값(0
)을 가져오고, React는 기꺼이 빈 값 대신0
을 렌더링합니다.
김정호
왜 하나의 태그로 감싸서 반환해야할까?
JSX는 html 마크업 언어가 아니라 객체입니다.
컴포넌트는 JSX를 반환하는 JS함수입니다.
그래서 2개 이상태그를 반환할때는 2개의 객체를 반환하는거라고 똑같이 생각하면 그 2개를 감싸는 객체를 감싸줘야지 반환이 됩니다.
fragment태그에 key값을 넣고 싶다면 어떻게 해야할까?
< key=...></>
하면 key값을 할당하지 못하고 오류가 나온다.
또한 직접 컴포넌트에서 key값은 할당하지 못한다!태그를 추가하고 싶지 않고 key값을 할당하기 위해서는
<Fragment key=””></Fragment>
이런식으로 할당하면 된다.react에서 렌더링 하는 중에 데이터를 읽을 수 있는 소스 3가지는 무엇일까요?
state,prorp,context
입니다.낙관적 업데이트나 사용자 입력에 대한 응답으로 무언가를 변경하는 예측이 가능한 순수한 함수형 컴포넌트를 제작하기 위해서 잘 활용해야한다고 생각합니다.이러한 입력들은 React의 "단방향 데이터 흐름" 원칙에 따라, 상위(부모) 컴포넌트로부터 하위(자식) 컴포넌트로 데이터가 흘러갑니다.
당신의 컴포넌트는 안녕하신가요?(한번씩 읽어보면 좋을것 같습니다!)
김다은
컴포넌트 안에 다른 컴포넌트를 정의하지 말 것??
당연하다고 막연하게만 생각했는데 왜 그러면 안되는거지? ⇒ 이유 : 매우 느리고 버그를 촉발시킨다. ⇒ 왜? Gallery를 렌더링할 때마다 다른 Profile 함수가 생성되기 때문이다 (이해가 잘안가는 부분) ⇒ 이는 state가 렌더링될때마다 초기화 된다는 의미 ⇒ 결론 : 버그와 성능 이슈
나의 문제 : 리액트의 렌더링이 어떤식으로 이루어지는지 잘 모르겠슴돠..?🥲
차근차근 다시 짚어보기!!
리액트에서 각 컴포넌트는 자체적으로 상태와 렌더링을 관리하며, 상태가 변경될 때 해당 컴포넌트만 리렌더링됩니다.
렌더링은 컴포넌트가 props와 state를 통해 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업이다.
즉, props와 state가 변하면 렌더링이 일어난다는 것
Gallery를 렌더링 할때마다(props와 state가 변하면) 다른 Profile 함수가 생성된다.
비교를 해보자!!
왜 여러 JSX태그를 하나로 감싸줘야 할까요?
이유 : 함수에서는 두개의 객체를 반환할수 없어서
return {},{}가 불가능
return [{},{}] 라면 가능할지도?ㅋㅋㅋㅋ이건 테스트 안해봐서 모르겠당..

실제로 App함수는 위와같이 객체로 변환된다.
JSX 태그안에 쓰는 어트리뷰트는 왜 카멜케이스로 작성할까?
JSX로 작성된 어트리뷰트는 JavaScript 객체의 키가 되고 객체의 키값으로 변수명이 들어가는데 자바스크립트의 변수명에는 대시를 포함하면 안되는 규칙이 있기때문
다만 역사적인 이유로 aria- 와 data-의 어트리뷰트는 대시 사용가능…역사까지;;;이건 그냥 그런갑보다 하고 넘겨야겠다;;
Props는 읽기 전용 스냅샷이다.
위에서 props는 항상 고정되어있지않지만 불변이라고 말하는데
Props는 읽기 전용 스냅샷으로, 렌더링할 때마다 새로운 버전의 props를 받습니다.
Props는 변경할 수 없습니다. 상호작용이 필요한 경우 state를 설정해야 합니다.
항상 새로운 버전의 props를 받는것이 고정되어있지않다는 뜻이고
props를 변경할수없다는게 불변이라는 뜻인것같음 ⇒ 컴포넌트 내부에서 직접적으로 props를 변경할수없다는 뜻이다.
리액트에서의 if else 와 삼항연산자 그리고 인스턴스 (이거 뭔소리인지 모르겠음!!!)
위의 예제는 동일한가? ⇒ 그렇다 ⇒ 이유 :
객체 지향 프로그래밍에 익숙하다면, 위의 두 예제 중 하나가 <li>의 서로 다른 두 “인스턴스”를 생성할 수 있기 때문에 미묘하게 다르다고 생각할 수 있습니다.
(이해가 안가는부분)
하지만 JSX 요소는 내부 state를 보유하지 않고 실제 DOM 노드가 아니기 때문에 “인스턴스”가 아닙니다.
위의 글을 이해하기 위해서 먼저 객체지향 프로그래밍의 인스턴스에 대해 알아야한다.
객체 지향 프로그래밍이란?
자바스크립트를 이야기한다. 정확히 자바스크립트는 프로토타입 기반의 객체지향 프로로그래밍 언어이다.
객체 :
분류
, 프로토타입 : 유사성
인스턴스 : 이 부분이 제일 이해가 안가긴하는데 자바스크립트에서 createElement()메서드를 사용하면 서로 다른 “인스턴스”를 생성할 수 있다고 한다.(정확하지 않음! 위의 if esle코드에 써놓은것처럼 서로 다른 인스턴스를 가지고있지 않을까 추측하고있다. ) 하지만 리액트에서는 실제 DOM 노드가 아니기때문에 인스턴스가 아니다.
gpt 왈
이 문장은 React에서 JSX 요소와 객체 지향 프로그래밍에서의 "인스턴스" 개념 사이의 차이를 설명하고 있습니다.
객체 지향 프로그래밍에서, 클래스로부터 생성된 객체는 각각 독립적인 "인스턴스"입니다. 이 인스턴스들은 서로 다른 상태를 가질 수 있고, 하나의 인스턴스가 변경되더라도 다른 인스턴스에는 영향을 미치지 않습니다.
그러나 React에서 JSX 요소(
<li>
등)는 "인스턴스"라기보다는 렌더링을 위한 "설명서" 또는 "청사진(blueprint)"에 가깝습니다. JSX 요소 자체는 내부 상태를 가지지 않으며, 실제 DOM 노드도 아닙니다. 따라서 같은 JSX 요소를 여러 번 생성하더라도 각각 독립적인 상태를 가지지 않습니다.즉,
<li>Item 1</li>
과 <li>Item 2</li>
가 있을 때, 이들은 서로 다른 '상태'를 가진다거나 서로 영향을 주거나 받지 않습니다. 그저 화면에 어떻게 표시될 것인가에 대한 정보(청사진)일 뿐입니다.마지막 부분의 "state 보존 및 재설정에서 작동 방식에 대해 자세히 설명합니다." 문장은 React 컴포넌트의 상태 관리 방식과 관련된 추가 정보를 제공한다는 의미입니다. 컴포넌트의 state가 어떻게 보존되고 재설정되는지 등에 대한 내용을 참조하라는 것입니다.
&&
의 왼쪽에 숫자를 넣지 마세요.
0자체를 렌더링한다고? 맞음 0 자체를 렌더링하는게
모든 리액트 컴포넌트는 순수함수처럼 동작해야한다.
순수 함수란?
😇 Good Example : 순수 함수
🤬 Bad Example : Side Effects(의도하지않은 결과)
왜 2,4,6이 나오는가?? ⇒ StrictMode때문이다 ⇒ 왜? StrictMode는 컴포넌트 함수를 두번 호출함으로써 순수하지 않은 계산을 감지한다. 순수함수는 계산만 하므로 두번 호출해도 아무것도 바뀌지않는 반면, 순수하지않은 함수는 두번 호출하면 함수가 손상된다.
React에서는 렌더링하는 동안 읽을수있는 입력이 세가지가 있다.
props, state, context
이러한 입력은 항상 읽기 전용으로 취급해야 한다. 컴포넌트가 렌더링되는 동안에는 기존 변수나 객체를 절대 변경해서는 안된다.⭐️⭐️⭐️지역변이
⚠️ 변이 : 컴포넌트가 렌더링하는 동안 기존 변수를 변경하는것
렌더링 하는 동안 “방금”생성한 변수와 객체를 변경하는 것은 괜찮다! ⇒ 왜? TeaGathering내부에서 동일한 렌더링 중에서 생성됐기 때문이다. 이를 지역변이라고 한다.
사이드 이펙트를 일으킬 수 있는 곳
이벤트 핸들러는 렌더링 중에 실행되지 않기때문에 순수할 필요가 없다.
왜 리액트는 순수성을 중요시 할까?
- 동일한 입력에 대해 동일한 결과를 반환하기때문에 하나의 컴포넌트가 많은 사용자 요청을 처리할수있다.
- 항상 동일한 결과를 반환하기때문에 캐싱해도 안전하다. (memo와 같은 훅을 통해 렌더링 건너뛰기 가능)
- 순수성 덕분에 언제든지 계산을 중단해도 안전하다.
상호작용 추가하기
김다은
함수는 호출하는게 아니라 전달되어야 한다.
handleClick()은 렌더링 중에 즉시함수를 실행한다.
지역변수대신 state를 써야하는 이유!
let index = 0;
와 같은 지역변수를 쓰면안되는 이유- 지역변수는 렌더링 간에 유지되지 않는다. ( index가 다시 초기화된다)
- 지역변수를 변경해도 렌더링을 발동시키지 않는다. (index가 바뀌어도 렌더링을 발동시키지 않는다.)
react는 어떤 state를 반환할지 어떻게 아는걸까?
useState호출은 어떤 state 변수를 참조하는지에 대한 정보를 받지 못한다. 그렇다면 useState에 전달되는 “식별자”가 없는데 어떤 state변수를 반환할지 어떻게 알수있을까?
답은 배열에 있다!?
- useState를 호출할때마다
[0, setState]
와같은 pair쌍을 만들고 인덱스를 증가시킨다.
- componentHooks에 parir를 저장한다.
componentHooks = [Array(2), Array(2)]
- setState를 호출할때마다 pair에 새값을 넣고 DOM을 새로 그린다.
- DOM이 새로그려질때마다
currentHookIndex = 0
와같이 currentHookIndex를 초기화한 후, Gallery()를 재렌더링한다.
⇒ 이때, setState를 호출할때 DOM을 새로 그리는것이 중요하다! ⇒ 이유 : DOM이 새로 그려지면서 currentHookIndex가 초기화 되는데 이는 useState 호출 순서를 초기화 시킨다.⇒
훅은
동일한 컴포넌트의 모든 렌더링에서 안정적인 호출 순서에 의존합니다.
”훅은 항상 같은 순서로 호출된다”가 핵심state는 스냅샷처럼 동작한다.
alert(number)
의 값은 업데이트되기 전의 값이다.⇒ 왜? state 변수의 값은 렌더링 내에서 절대 변경되지 않는다. setNumer가 호출된 후에도 number의 값은 계속 0인데 이는 컴포넌트를 호출해 리액트가 UI의 스냅샷을 찍을때 고정된 값이기 때문이다.react는 state 업데이트를 하기 전에 이벤트 핸들러의 모든 코드가 실행될때까지 기다린다.
객체 state
기술적으로 객체 자체의 내용을 변경하는 것은 가능하다. (객체 타입은 변경가능한 값이라서) 하지만, 직접 변경하는것은 권장하지 않는다. ⇒ 이유 : react가 객체가 변이되었다는 사실을 알지 못한다. (리렌더링을 촉발시키지 못함)
하나의 필드만 업데이트하고 다른 필드들은 이전값을 유지하는 방법
여러 필드에 단일 이벤트 핸들러 사용하기
김정호
리액트에서 이벤트 캡처링 사용하는 방법?왜 사용해야할까?
버블링은 많이 사용되나 캡처링은 잘 사용되지 않는다.
왜일까?
캡처링은 상위 이벤트에서 하위 이벤트까지 하나씩 내려가면서 실행되는거다.위에서 아래로 이벤트가 실행된다고 생각하면 편하다.이거를 언제 사용할수 있을까? 거의 사용할 일이 없다고 생각한다.
하지만 버블링은 캡처링과 반대로 아래에서 위로 올라가는 거다.
한번만 이벤트 선언으로 추상화할 수 있고 이거를 모르면 이벤트가 여러번 선언이 될수 있다.
지역 변수를 통해서 알아보는 상태를 정의해야하는 이유
지역 변수는 렌더링 촉진과 상태를 유지하지 못한다.
변화를 주고 싶다면 useState 훅을 통해서 상태와 상태를 변화시키는 함수를 이용하면 된다.
React는 어떤 state를 반환할지 어떻게 알 수 있을까요?
컴포넌트마다 훅이 있다면 순서대로 상태와 그 상태를 변화시키는 함수가 있는 pair가 index순으로 담겨져 있다.→ 훅은 동일한 컴포넌트의 모든 렌더링에서 안정적인 호출 순서에 의존하는 이유.
결국 순서대로 어떤 상태가 있는지 알수 있다.
렌더링은 항상 순수한 계산이어야 합니다!!!!!
- 동일한 입력에는 동일한 출력을 해야합니다. 동일한 입력이 주어지면 컴포넌트는 항상 동일한 JSX를 반환해야 합니다.
- 이전의 state를 변경해서는 안됩니다. 렌더링 전에 존재했던 객체나 변수를 변경해서는 안 됩니다.
“렌더링”이란 React가 컴포넌트, 즉,함수를 호출한다는 뜻입니다. 해당 함수에서 반환하는 JSX는 시간상 UI의 스냅샷과 같습니다. prop, 이벤트 핸들러, 로컬 변수는 모두 렌더링 당시의 state를 사용해 계산됩니다.
react는 렌더링이 촉발되면 바로 업데이트가 된다.그 업데이트된 스냅샷을 바탕으로 비교해서 ui를 보여준다.그래서 렌더링 되기 전 상태를 바탕으로 이벤트 핸들러,props가 참고되서 업데이트 된다!
React는 state 업데이트를 하기 전에 이벤트 핸들러의 모든 코드가 실행될 때까지 기다립니다.
상태를 업데이트하기 전에 react의 state를 큐를 담는다.이것을 일괄처리해서 비용을 낮추고 빠르게 실행할수 있게한다.→이벤트 핸들러 함수가 처리가 안될경우에는 ui 업데이트가 안된다!
React는 클릭과 같은 여러 의도적인 이벤트에 대해 일괄 처리하지 않으며, 각 클릭은 개별적으로 처리됩니다. → 여러번 클릭해도 그 클릭에 대해서 개별적으로 처리한다!
변이란 무엇인가?
문법 자체로도 문제가 없고 값도 잘 변경된다.이거를 변이라고 한다.
하지만 다시 리렌더링이 되지 않고 state는 읽기전용 상태를 유지해야되기때문에 이렇게 작성하면 안되고 원시값처럼 복사를 해야한다.
...
전개 구문은 “얕은” 구문으로, 한 단계 깊이만 복사한다는 점에 유의하세요. 속도는 빠르지만 중첩된 프로퍼티를 업데이트하려면 두 번 이상 사용해야 한다는 뜻이기도 합니다.
찬욱님과 다은님이 잘 정리한 내용!잘 참고하자!
배열 변이를 피해하는 메소드들
ㅤ | 비추천 | 추천 |
추가 | push , unshift | concat ,
[...arr] spread syntax
ex) push: […arr,{…}],
unshift: [{…},…arr] |
삭제 | pop , shift , splice | filter , slice |
교체 | splice , arr[i] = ... | map |
정렬 | reverse , sort | 배열을 복사한 다음 처리 |
배열을 복사하더라도 배열 내부의 기존 항목을 직접 변이할 수는 없습니다.
정찬욱
이벤트에 응답하기
- 이벤트 핸들러에 적절한 HTML 태그를 사용해야 합니다. 기본 브라우저 스타일이 마음에 들지 않고 링크나 다른 UI 요소처럼 보이길 원한다면, CSS를 사용하여 원하는 방식으로 조정할 수 있습니다.
- 렌더링 함수와 달리 이벤트 핸들러는 순수할 필요가 없으므로 타이핑에 대한 응답으로 input 값을 변경하거나 버튼 누름에 대한 응답으로 목록을 변경하는 등 무언가를 변경하기에 좋은 곳입니다.
state: 컴포넌트의 메모리
- 지역 변수는 렌더링 간에 유지되지 않고 변경해도 렌더링을 발동시키지 않습니다..
- 훅은 렌더링 중일 때만 사용할 수 있는 특별한 함수이다. 또한 컴포넌트의 최상위 레벨 또는 커스텀 훅에서만 호출할 수 있습니다.
- state 변수는 컴포넌트의 리렌더링 사이에 정보를 유지하는 데만 필요합니다. 단일 이벤트 핸들러 내에서는 일반 변수로도 충분합니다. 일반 변수가 잘 작동할 때는 state 변수를 도입하지 마세요.
렌더링하고 커밋하기
- 컴포넌트 렌더링은 첫 렌더링인 경우와 state가 업데이트된 경우입니다.
- 이전의 state를 변경해서는 안됩니다. 렌더링 전에 존재했던 객체나 변수를 변경해서는 안 됩니다.
스냅샷으로서의 state
- prop, 이벤트 핸들러, 로컬 변수는 모두 렌더링 당시의 state를 사용해 계산됩니다.
- 컴포넌트는 해당 렌더링의 state 값을 사용해 계산된 새로운 props 세트와 이벤트 핸들러가 포함된 UI의 스냅샷을 JSX에 반환합니다.
- state를 설정하면 다음 렌더링에 대해서만 변경됩니다.
- state 변수의 값은 이벤트 핸들러의 코드가 비동기적이더라도 렌더링 내에서 절대 변경되지 않습니다.
여러 state 업데이트를 큐에 담기
- React는 state 업데이트를 하기 전에 이벤트 핸들러의 모든 코드가 실행될 때까지 기다립니다.
- React는 클릭과 같은 여러 의도적인 이벤트에 대해 일괄 처리하지 않으며, 각 클릭은 개별적으로 처리됩니다.
- 업데이터 함수는 렌더링 중에 실행되므로, 업데이터 함수는 순수해야 하며 결과만 반환해야 합니다. 업데이터 함수 내부에서 state를 변경하거나 다른 사이드 이팩트를 실행하면 안됩니다.
객체 state 업데이트
- 객체를 중첩된 객체라고 생각하면 이점을 이해하기 어렵습니다. 실은 프로퍼티를 사용하여 서로를 가리키는 별도의 객체입니다.
- state에 객체를 저장하면 객체를 변이해도 렌더링을 촉발하지 않고 이전 렌더링 스냅샷의 state가 변경됩니다.
배열 state 업데이트
- 배열 전개 구문을 사용하면 항목을 원본 배열 앞에 배치하여 항목을 추가할 수도 있습니다.
- 일반적으로 state를 몇 레벨 이상 깊이 업데이트할 필요는 없습니다. state 객체가 매우 깊다면 다르게 재구성하여 평평하게 만드는 것이 좋습니다.
깊은복사와 얕은복사
다은
ㅤ | 원시타입 | 객체타입 |
값 변경 | 불가능 | 가능 |
메모리공간 | 실제 값 저장 | 참조 값 저장 |
다른 변수에 할당시 | 원시 값 복사 | 참조 값 복사 |
원시 타입과 객체타입
- 값 변경
- 원시 타입 변경 : 불가능
- 객체 타입 변경 : 가능 객체는 변경가능한 값이므로, 객체를 직접 수정할수있다. 이때 객체를 할당한 변수에 재할당을 하지 않았으므로, 변수의 참조값은 변경되지 않는다.
원시 값은 변경 불가능한 값이므로, 원시값을 변경하려면 재할당을 통해 메모리에 원시 값을 새롭게 생성해야한다. 이때 메모리 공간의 주소가 바뀐다.


- 메모리 공간
- 원시 타입 할당 : 실제 값 저장
- 객체 타입 할당 : 참조 값 저장
- 다른 변수에 할당시
- 원시 타입 할당 (깊은 복사) : 값에 의한 전달 다른 메모리 공간에 저장된 별개의 값이기때문에 score 변수값을 변경해도 copy에 어떠한 영향도 주지않는다.
- 객체 타입 할당 (얕은 복사) : 참조에 의한 전달 저장된 메모리 주소는 다르지만 동일한 참조값을 갖는다. 이것은 두개의 식별자가 하나의 객체를 공유하는 것을 의미한다.


깊은 복사와 얕은 복사
객체의 경우 한단계까지만 복사하는 경우 얕은 복사
객체에 중첩되어있는것까지 복사하는 경우 깊은 복사

Spread syntax effectively goes one level deep while copying an array.
스프레드 문법이 1단계 레벨에서는 깊은 복사로 작동하는 것 같다.
자바스크립트에는 “참조에 의한 전달”은 존재하지않고 “값에 의한 전달”만이 존재한다.
앗 뭔소리야~~
모르겠고 이거만 외우자! ⭐️ ⭐️ ⭐️ ⭐️
다른 변수에 할당시 원시타입은 다른 메모리 공간에 저장된 별개의 값이기 때문에 영향 없음
반면, 객체타입은 동일한 참조값을 갖기때문에 영향을 줌
스프레드 연산자는 1단계에서는 깊은 복사로 동작함
state 관리
김다은
리액트는 ui를 조작하는 선언적인 방법을 제공한다.


선언형 프로그래밍은 UI를 세밀하게 관리(명령형)하지 않고 각 시각적 상태에 대해 UI를 기술하는 것을 의미한다. (즉, 리액트에서는 직접 ui를 조작하지않고 상태로 관리한다.) 이를 위해서 컴포넌트의 다양한
“시각적 상태”
를 식별해야한다props를 state에 그대로 미러링하지 마세요
다음코드는 중복 state의 일반적인 예시이다.
다음코드는 중복 state의 일반적인 예시이다. ⇒ 이유 :부모 컴포넌트가 나중에 다른
messageColor
값(예: blue
대신 red
)을 전달하면 color
state변수가 업데이트되지 않는다! state 중복을 피하세요
제어 및 비제어 컴포넌트
비제어 컴포넌트 : 로컬 state를 가진 컴포넌트 ⇒ ref를 사용
제어 컴포넌트 : props에 의해 구동되는 컴포넌트 (부모컴포넌트가 제어하는 경우) ⇒ setState를 사용
react는 ui트리에서 어떤 컴포넌트가 어떤 state에 속하는지를 추적합니다.
- 컴포넌트에서 리액트는 ui트리를 생성하고, 이 트리는 react DOM이 DOM을 렌더링하는데 사용된다.
- state는 컴포넌트 내부가 아닌 react 내부에서 유지된다. react는 ui트리에서 해당 컴토넌트가 어디에 위치하는지에 따라 보유하고 있는 각 state를 올바른 컴포넌트와 연결한다.
위의 내용을 정리하자면 리액트는
⭐️⭐️⭐️ ui트리를 생성 후 ⇒ 모든 state를 유지하고 있다가 ⇒ 각 state가 어떤 컴포넌트에 속하고 있는지 추적 ⇒ 만약 같은 컴포넌트가 같은 위치에 렌더링된다면 state를 유지 혹은 다르다면 state 재설정한다.
주의!
전체코드를 보려면 공식문서를 확인해주세요! 또한 이유가 정확하지 않을 수 있습니다!
문제 1 . Counter 컴포넌트의 state가 독립적인 이유는?
문제2. 두 번째 Counter컴포넌트가 다시 렌더링될때마다 state가 초기화 되는 이유?
리액트에서 중요한것은 JSX마크업이 아니라 UI트리에서의 위치입니다!
두 경우 모두 Counter를 첫번째 자식으로 가진 div를 반환하기때문에 ui트리상 동일한 “주소”를 갖습니다.
동일한 위치의 다른 컴포넌트를 state를 초기화합니다.
또한, 같은 위치에서 다른 컴포넌트를 렌더링하면 전체 하위트리의 state가 재설정된다
그렇기 때문에 컴포넌트 함수 정의를 중첩하면 안된다.
state 재설정하는 방법
reducer
reducer를 사용한 state관리는 state를 직접 설정하는 것과 약간 다르다. state를 설정하여 리액트에게 “무엇을 할지”를 지시하는 대신, 이벤트 핸들러에서 “action”을 전달하여 “사용자가 방금 한 일”을 지정한다. 그리고 react는 reducer로부터 반환되는 것을 state로 설정할것이다.
동일한 컴포넌트에서 context 사용 및 제공
context : porps를 전달하지 않고도 트리에서 데이터를 필요한 컴포넌트로 “텔레포트” 하는 방법 🧙♀️
context를 사용하면 위의 컴폰너트에서 정보를 읽을수있으므로 각 section은 위의 section에서 level을 읽고 level +1을 자동으로 아래로 전달할수있다.
context는 중간 컴포넌트들을 통과합니다.
김정호
선언형 vs 명령형
선언형은 무엇을 보여주느냐?(react는 이걸 중점적으로 생각)
명령형은 어떻게 보여주느냐?
react는 직접적으로 ui을 조작하지 않습니다.즉, 컴포넌트를 직접 활성화하거나 비활성화 하지도, 보여주거나 숨기지도 않습니다. 대신 표시할 내용을 선언하면 React가 UI를 업데이트할 방법을 알아냅니다. 택시를 타고 기사에게 정확히 어디서 꺾어야 할지를 알려주는 대신 어디로 가고 싶은지만 말한다고 생각해 보세요. 목적지까지 데려다주는 것은 택시기사의 몫이며, 기사는 여러분이 미처 생각하지 못한 지름길을 알고 있을 수도 있습니다!
state 어떻게 나눠야할까?
가능한 state는 많이 선언하면 오류나 예상치 못한 버그를 유발하기 쉽다!
- 컴포넌트의 다양한 시각적 상태를 식별합니다. → 일단 많이 적자!
- 상태 변화를 촉발하는 요소를 파악합니다.
useState
를 사용하여 메모리의 상태를 표현합니다.
- 비필수적인 state 변수를 제거합니다.
- 이벤트 핸들러를 연결하여 state를 설정합니다.
일단 많이 상태를 생각하고 적자.그리고 불필요한 state는 삭제하면서 보완해나가야한다.
불필요한 state 기준?
- state가 모순을 야기하나요?
- 모순이란?
- 쇼핑 카트 앱에서 사용자가 특정 항목을 카트에 추가하였지만, 카트 아이콘에는 해당 항목이 반영되지 않았다면 이것은 상태의 모순입니다. 사용자가 수행한 동작과 시스템의 반응 사이에 불일치가 발생한 것입니다.
- 상태 모순을 방지하는 한 가지 방법은 "단일 진실 원천(Single Source of Truth, SSOT)" 원칙을 따르는 것입니다. 이 원칙에 따르면, 어떤 특정 데이터나 상태에 대해서 그 '진실'은 하나의 위치에서만 관리되어야 합니다. 여러 곳에서 동일한 데이터를 별도로 관리하는 것 대신 한 곳에서만 데이터를 업데이트하고, 필요한 곳에서 그 데이터를 참조(혹은 가져다 사용)합니다.
- 다른 state 변수에 이미 같은 정보가 있나요?
- 다른 state 변수를 뒤집으면 동일한 정보를 얻을 수 있나요?
state 구조화 원칙
- 관련 state를 그룹화합니다. 관련된 state(마우스 좌표,입력사항)이면 객체로 묶어서 진행
- state의 모순을 피하세요. → 클릭,보내는 중,완료 이런 상태를 하나씩 state 전달해서 진행하는게 아니라 ‘click’, ‘loading’ , ‘success’ 이런식으로 문자열로 할당하는것도 방법
- 불필요한 state를 피하세요. 렌더링 중에 컴포넌트의 props나 기존 state 변수에서 일부 정보를 계산할 수 있다면 해당 정보를 해당 컴포넌트의 state에 넣지 않아야 합니다.
- state 중복을 피하세요. 동일한 데이터가 여러 state 변수 간에 또는 중첩된 객체 내에 중복되면 동기화 state를 유지하기가 어렵습니다. 가능하면 중복을 줄이세요.
- 깊게 중첩된 state는 피하세요. 깊게 계층화된 state는 업데이트하기 쉽지 않습니다. 가능하면 state를 평평한 방식으로 구성하는 것이 좋습니다.
props를 state에 그대로 미러링하지 마세요
부모 컴포넌트가 나중에 prop에 다른 값을 전달하면 state 변수가 업데이트되지 않는다는 것입니다!
결국 동기화 문제!따로 변수로 담아서 참고하던가 아예 props 자체로 할당하는걸 추천!
훅이란 무엇일까?
훅이란 데이터를 재사용하는게 아니라 로직을 재사용하기 위해서 작성되는것 이다.
로직을 이용해서 나온 상태를 반환한다!
제어 및 비제어 컴포넌트
일반적으로 일부 로컬 state를 가진 컴포넌트를 “비제어 컴포넌트”라고 부릅니다.
반대로 컴포넌트의 중요한 정보가 자체 로컬 state가 아닌 props에 의해 구동되는 경우 컴포넌트가 “제어”된다고 말할 수 있습니다.
비제어 컴포넌트는 구성이 덜 필요하기 때문에 상위 컴포넌트 내에서 사용하기가 더 쉽습니다. 하지만 함께 통합하려는 경우 유연성이 떨어집니다. 제어 컴포넌트는 최대한의 유연성을 제공하지만 부모 컴포넌트가 props를 사용하여 완전히 구성해야 합니다.
react가 dom에 그리는 과정
react는 jsx로부터 ui 트리를 만들고 → react DOM은 해당 ui트리랑 일치하다록 dom 앨리먼트를 업데이트
리액트는 jsx 마크업이 아니라 ui트리에서 위치가 중요하다!
- 동일한 위치에 동일한 컴포넌트는 상태를 유지가 되는 이유!
React는 함수에서 조건을 어디에 배치했는지 알지 못합니다. 단지 여러분이 반환하는 트리만 볼 수 있을 뿐입니다.
리렌더링 사이에 state를 유지하려면 트리의 구조가 “일치”해야 합니다.
- 동일한 위치에 다른 state 유지하는 방법
- 다른 위치에 선언
- key값을 할당(key로 state 재설정하기)
- 서로 다른
key
를 부여했기 때문에 다르게 인식 - form 다룰때 좋다!
reducer는 왜 reducer일까?
reducer는 언제 사용해야할까?
context는 언제 사용해야할까?
context 잘 사용하는 방법?
정찬욱
state로 입력에 반응하기
- 선언형 프로그래밍은 UI를 세밀하게 관리(명령형)하지 않고 각 시각적 상태에 대해 UI를 기술하는 것을 의미합니다.
state 구조 선택
- 관련 state를 그룹화합니다.
- state의 모순을 피하세요.
- 불필요한 state를 피하세요.
- state 중복을 피하세요.
- 깊게 중첩된 state는 피하세요.
컴포넌트 간의 state 공유
- 컴포넌트를 (props에 의해) 제어할 지 (state에 의해) 비제어할지 고려해보는 것은 유용합니다.
state 보존 및 재설정
- React에서 중요한 것은 JSX 마크업이 아니라 UI 트리에서의 위치입니다.
- 리렌더링 사이에 state를 유지하려면 트리의 구조가 일치해야 한다.
- 항상 컴포넌트 함수를 최상위 수준에서 선언하고 정의를 중첩하면 안됩니다.
- key를 사용해 React가 모든 컴포넌트를 구분하도록 할 수 있습니다.
- key를 지정하면 React가 부모 내 순서가 아닌 key 자체를 위치의 일부로 사용하도록 지시합니다.
- key는 부모 내에서의 위치만 지정합니다.
state 로직을 reducer로 추출하기
- useState를 사용했을 때 코드가 복잡해지면 useReducer의 사용을 고려해보면 좋습니다.
- useReducer는 디버깅과 테스팅에 좋습니다.
- 일부 컴포넌트에서 잘못된 state 업데이트로 인해 버그가 자주 발생하고 코드에 더 많은 구조를 도입하려는 경우 reducer를 사용하는 것이 좋습니다.
- reducer는 반드시 순수해야 하고 각 action은 여러 데이터가 변경되더라도, 하나의 사용자 상호작용을 설명해 합니다.
- action type은 ‘state가 어떻게 변경되기를 원하는지’가 아니라 ‘사용자가 무엇을 했는지’를 설명하는 것이 이상적이라는 점을 명심하세요.
context로 데이터 깊숙이 전달하기
- context를 사용하기 전에
- props 전달로 시작하세요.
- 컴포넌트를 추출하고 JSX를 children으로 전달하세요.
reducer와 context로 확장하기
- state와 dispatch 함수를 위한 두 개의 context를 만듭니다.
탈출구
김다은
컴포넌트는 ref가 증가할 때마다 리렌더링되지 않는다
ref | state |
변경시 리렌더링을 촉발하지 않음 | 변경시 리렌더링을 촉발함 |
current 값을 수정하고 업데이트를 할수있음 | setting함수를 사용하여 state 변수를 수정해 리렌더링을 대기열에 추가해야함 |
렌더링 중에는 current 값을 읽거나 쓰지 않아야함 | 언제든지 state를 읽을수있음, 각 렌더링에는 변경되지 않는 자체 state snapshot이 있음 |
ref는 내부에서 어떻게 작동하나요?
객체지향 프로그래밍에 익숙하다면 인스턴스 필드를 떠올릴 수 있는데,
this.something
대신 somethingRef.current
를 사용하면 됩니다. ⇒ const somethingRef = useRef(null)
를 얘기하는것 같음ref 콜백을 사용하여 refs 목록을 관리하는 방법
반복문 또는 map()내부에서는 useRef를 호출할수없다. 이 문제를 해결하기 위해서는 ref 속성에 함수를 전달하는 것이다. 이를 ref 콜백이라고 한다.
react는 ref를 설정할때가 되면 DOM 노드로, 지울때가 되면 null로 ref 콜백을 호출한다. 이를 통해 Map을 유지관리하고 일종의 id로 모든 ref에 접근할수있다.
플러싱 state는 flushSync와 동기식으로 업데이트됩니다.
react에서는 state 업데이트가 큐에 등록된다. 그러나. setTodos가 dom을 즉시 업데이트 하지않기 때문에 문제가 발생한다. 따라서 목록을 마지막 요소로 스크롤할때 할일이 아직 추가되지않는 상태이기 때문에 스크롤이 항상 한 항목씩 “뒤쳐지는”이유가 된다.
이를 해결하기 위해서는 react가 dom을 동기적으로 업데이트(”플러시”)하도록 강제할수있다.
김정호
ref는 렌더링 로직에 사용하지 마세요!
정찬욱
ref로 값 참조하기
- 렌더링 중에는 current 값을 읽거나 쓰지 않아야 합니다.
- ref는 외부 시스템이나 브라우저 API로 작업할 때 유용합니다.
- ref의 현재 값을 변이하면 즉시 변경됩니다.
ref로 DOM 조작하기
- ref 콜백을 사용하여 refs 목록을 관리할 수 있습니다.
- DOM 노드를 노출하길 원하는 컴포넌트에 해당 동작을 설정하고 싶을땐
forwardRef
를 사용하면 됩니다.
- 노출되는 기능을 제한하고 싶은 경우
useImperativeHandle
을 사용하면 됩니다.
- state 업데이트를
flushSync
호출로 감싸면 DOM을 동기적으로 업데이트할 수 있습니다.
- React가 관리하는 DOM 노드를 변경하면 안됩니다.
- React가 업데이트할 이유가 없는 DOM의 일부는 안전하게 수정할 수 있습니다.
Effect와 동기화하기
- Effect를 사용하면 특정 이벤트가 아닌 렌더링 자체로 인해 발생하는 사이드 이펙트를 명시할 수 있습니다.
useEffect
는 해당 렌더링이 화면에 반영이 될 때까지 코드 조각의 실행을 지연합니다.
- React는 다음 Effect가 실행되기 전 및 마운트 해제 시점에 클린업 함수를 호출합니다.
Effect가 필요하지 않을 수도 있습니다.
- 컴포넌트가 사용자에게 표시되었기 때문에 실행되어야 하는 코드에만 Effect를 사용하세요.
- 경쟁 조건을 수정하기 위해서는 오래된 응답을 무시하도록 클린업 함수를 추가해야 합니다.
반응형 Effect의 생명주기
- 각 Effect를 컴포넌트의 생명주기와 독립적으로 생각해보세요.
- 컴포넌트를 마운트, 업데이트 또는 마운트 해제하는 것은 중요하지 않습니다. 동기화를 시작하는 방법과 중지하는 방법만 설명하면 됩니다.
- 코드의 각 Effect는 별도의 독립적인 동기화 프로세스를 나타내야 합니다.
- 컴포넌트 내부의 모든 값(컴포넌트 본문의 props, state, 변수 포함)은 반응형입니다. 모든 반응형 값은 다시 렌더링할 때 변경될 수 있으므로 반응형 값을 Effect의 의존성으로 포함시켜야 합니다.
location.pathname
과 같은 변이 가능한 값은 의존성이 될 수 없습니다.
ref.current
와 같이 변이 가능한 값 또는 이 값으로부터 읽은 것 역시 의존성이 될 수 없습니다.
- 의존성을 선택할 수는 없습니다.
이벤트와 Effect 분리하기
- 이벤트 핸들러는 특정 상호 작용에 대한 응답으로 실행됩니다.
- Effect는 동기화가 필요할 때마다 실행됩니다.
- 이벤트 핸들러 내부의 로직은 반응형이 아닙니다.
- Effect 내부의 로직은 반응적입니다.
Effect 의존성 제거하기
- 의존성 목록을 변경하려면 코드를 변경하세요.
- 의존성 린트 오류는 컴파일 오류로 처리하는 것이 좋습니다.
- 다음 state를 계산하기 위해 업데이터 함수를 전달하세요.
- JavaScript에서는 새로 생성된 객체와 함수가 다른 모든 객체와 구별되는 것으로 간주됩니다.
- 객체 및 함수 의존성으로 인해 Effect가 필요 이상으로 자주 재동기화될 수 있습니다.
- Effect 외부의 객체에서 정보를 읽고 객체 및 함수 의존성을 피하십시오.
커스텀 훅으로 로직 재사용하기
- 훅을 호출하지 않는 함수는 훅이 될 필요가 없습니다.
- 커스텀 Hook의 코드를 컴포넌트 본문의 일부로 생각하세요.
- 커스텀 훅은 컴포넌트와 함께 다시 렌더링되기 때문에 항상 최신 props와 state를 받습니다.
- Effect를 커스텀 훅으로 감싸면 의도와 데이터 흐름 방식을 정확하게 전달할 수 있습니다.
- 좋은 커스텀 훅은 호출 코드가 수행하는 작업을 제한하여 보다 선언적으로 만듭니다.