[TIL] React Lifting state up, Effect Hook

ㅜㅜ·2022년 10월 11일
1

Today I learn

목록 보기
30/77
post-thumbnail

state 끌어올리기

React에서는 페이지를 만들기 이전에 컴포넌트를 만들고, 조립하는 상향식 방법을 사용. (테스트 용이, 확장성.)

단일 책임 원칙으로 한 요소에서는 한 기능만을 책임지도록 함.

데이터는 단방향으로 흐르게 만들어 모든 state는 항상 특정한 컴포넌트가 소유하고, 그 state로부터 파생된 UI 또는 데이터는 오직 트리구조에서 자신의 '아래'에 있는 컴포넌트에만 영향을 미칠 수 있음.


❗️ 하지만 ! 부모 컴포넌트 상태가 하위 컴포넌트에 의해 변하는 경우가 존재!

이런 역방향 데이터 흐름을 해결하기 위해 'Lifting state up'을 사용할 수 있다.

✅ 상태 변경 함수를 하위 컴포넌트에 props로 전달 & 이 함수를 하위 컴포넌트가 실행하도록 한다.

하위 컴포넌트는 props로 받은 데이터가 state인지 아닌지 알 수 없기 때문에 본래는 부모 요소에 있는 상태 변화 함수를 사용할 수 없지만, 위와 같은 방법을 쓰면 가능함.





🤔 과제 : states airline client part-1

  • 과제에서 Lifting state up을 사용한 부분
    : 요소 Main은 Search보다 상위의 요소임. Main에서 사용하는 state 중 하나의 상태 변화 함수를 Search 요소의 속성으로 전달해주고, Search.js에서 실행되도록 만들었음.

  • 키와 값이 같으면 생략할 수 있다
    : 아무생각 없이 setCondition 함수의 인자로 {departure, destination}만 적었던 것 같아서 메모.
    이렇게 적을 수 있었던 것은 키와 값이 같아서 생략할 수 있었던 것!

  • 문제를 풀기 전 코드를 제대로 읽어보고, 코드 간의 관계를 파악하기
    : 마지막 onSearch 함수를 handleSearchClick 함수에 넣어주면서 departure, destination의 키에 대한 값을 어떻게 넣어야 할 지 고민했는데, 그 아래 Search 함수의 리턴 값을 잘 살펴보면 input들의 value들이 각각 어떤 녀석들인지 확인할 수 있다. (혹은 Search 함수 내부 useState를 확인해도 좋았을듯)





Effect Hook

  • side effect (부수 효과)

: 함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 side effect가 있다고 이야기함.

let foo = 'hello';

function bar() {
  foo = 'world';
}

bar(); // bar는 Side Effect를 발생시킴.
//함수 외부의 전역 변수인 foo 값을 변화시키니까.
  • pure function (순수 함수)

: 오직 함수의 입력만이 함수의 결과에 영향을 주며, 입력으로 전달된 값을 수정하지 않는 함수.

순수 함수는 어떤 전달 인자가 주어질 경우, 항상 똑같은 값이 리턴됨을 보장하고, 그래서 예측 가능한 함수이다.

function upper(str) {
  return str.toUpperCase(); // toUpperCase 메소드는 원본을 수정하지 않습니다 (Immutable)
}

upper('hello') // 'HELLO'


❓ 질문

  • Math.random()이 순수함수가 아닌 이유?
    : 해당 함수는 동일한 전달 인자에 동일한 값이 리턴 됨을 보장하지 않는다. 즉, 예측 가능하지 않다.
    Math.random()은 0과 1사이의 랜덤한 실수를 리턴하는데 전달 인자를 따로 넣어서 사용하지 않는다. 그러나 항상 전달 인자가 없는데 다른 값들이 리턴된다.

  • 어떤 함수가 fetch API를 이용해 AJAX 요청을 한다고 가정할 때 이 함수는 순수 함수가 아닌 이유?
    : 네트워크 상황, 서버상태에 따라 응답코드가 달라지기 때문에 예측이 불가능.

  • 어떤 함수가 서버에 네트워크 요청을 보낸다면 이로 인해 서버의 데이터에 side effect를 일으킬 수 있고, 이에 따라 해당 함수는 순수 함수가 아님.

React에서 서버로 네트워크 요청을 보내야 하는 경우가 있는데, 이러한 Ajax 요청을 처리할 때 Side Effect를 최소화하기 위해서 Effect Hook을 사용.
⇒ 만약 훅을 사용하지 않고 네트워크 요청 시, 그 동안 페이지가 멈추거나 깜빡일 수 있음.





Effect Hook

  • useEffect 의 첫번째 인자는 함수이며, 해당 함수 내에서 side effect를 실행하면 됨.
  • 매번 새롭게 컴포넌트가 랜더링 될 때 Effect Hook이 실행됨.
  • Hook 사용 주의점
    • 최상위에서만 Hook을 호출 (반복문, 조건문, 중첩된 함수 내에서 호출 x) ⇒ 컴포넌트가 랜더링 될 때마다 항상 동일한 순서로 Hook 호출되는 순서 보장.
    • React 함수 내에서 Hook을 호출


🟢 조건부 effect 발생 (dependency array)

  • useEffect의 두번째 인자는 배열이며, 배열은 조건을 담고 있다.

    조건은 (boolean형태x)‘어떤 값의 변경이 일어날때’를 말하므로, 배열에는 ‘어떤 값’의 목록이 들어감.

    이 배열을 특별히 ‘종속성 배열’이라고 부름.

  • 종속성 목록에 아무런 종속성도 없을 때(두번째 배열이 빈 배열일 때) 컴포넌트가 처음 생성될 때만 effect 함수가 실행.

    ⇒ 이러한 빈 배열은 대표적으로 외부 API를 통해 리소스를 받아오고 더이상 API 호출이 필요하지 않을 때 사용할 수 있음.

    ⇒ 아무것도 넣지 않을 때는 컴포넌트가 처음 생성되거나, props가 업데이트되거나, state가 업데이트 될 때 effect 함수가 실행됨.

//1.빈 배열 
//처음 생성될 때만 
useEffect(함수,[])


//2.아무것도 넣지 않을 때 (기본 형태)
//처음 생성, props 업데이트, state 업데이트 시 실행
useEffect(함수)




🤔 필터링 예제

: 명언 목록이 있고, 필터에 해당 명언에 포함된 문자와 일치하는 문자가 있을 경우 필터링 해서 보여주고자 할 때, 목록 내 필터링 구현하기 위해 두 가지 접근 방법이 존재.

  1. 컴포넌트 내에서 필터링 : 전체 데이터 목록 불러오고, 목록을 검색어로 필터링. ⇒ useEffect 함수에 빈 배열

  2. 컴포넌트 외부에서 필터링 : 컴포넌트 외부로 API 요청을 할 때 필터링한 결과를 받아오는 방법 (보통 서버에 매번 검색어와 함께 요청하는 경우) ⇒ useEffect 함수 배열에 filter라는 종속성 부여



🤔 위의 두 방식 차이점

컴포넌트 내부에서 처리

  • 장점 : HTTP 요청 빈도 줄임
  • 단점 : 브라우저(클라이언트) 메모리 상에 많은 데이터 갖게 되므로 클라이언트의 부담 늘어남.

컴포넌트 외부에서 처리

  • 장점 : 클라이언트가 필터링 구현 생각하지 않아도 됨
  • 단점 : 빈번한 HTTP 요청 + 서버가 필터링 처리 ⇒ 서버 부담




로딩 화면(loading indicator) 필요성


  • 모든 네트워크 요청이 항상 즉각적 응답을 가져다주는 것이 아니므로, 외부 API 접속이 느릴 경우를 고려해 로딩 화면(loading indicator) 구현이 필수적.

  • 상태 처리를 이용해 구현할 수 있음.

const [isLoading, setIsLoading] = useState(true);

//생략, LoadingIndicator 컴포넌트는 별도로 구현했음을 가정

return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}


  • fetch 요청 전후로 setIsLoading 설정해주어 보다 나은 UX 구현 가능.
//위 예시에서 이어짐 
useEffect(()=>{
	setIsLoading(true); //fetch 요청 전 로딩 화면부터 띄워줌 
	fetch(`http://서버주소/proverbs?q=${filter}`)
		.then(resp=> resp.json())
		.then(result => {
				setProverbs(result);
				setIsLoading(false);
			});//fetch요청이 성공했을 때 로딩화면 띄우는 상태를 false로 바꿈 
	},[filter]);//필터 값이 변경될 때 실행됨.
profile
다시 일어나는 중

0개의 댓글