리액트에서 props는 프로퍼티(properties, 속성)의 줄임말로, 읽기만 가능하며(read-only)불변성을 지닌 요소를 의미한다.
애플리케이션에서 props
는 부모 컴포넌트부터 자식 컴포넌트로 전달되는 계층 구조를 따른다. 반대는 불가능하다.
* 리액트는 단방향 데이터 흐름 모델을 사용하기 때문이다.
리액트의 라이프사이클은 4가지로 설명할 수 있다.
Key Ponint 💡
mounnt
,render
,update
,unMount
- 여기서
mount
는 DOM이 생성되고 웹 브라우저 상에서 나타나는 것을 뜻하고,- DOM이 제거되는 것은
unMount
를 의미한다.
componentDidMount()
render()
가 있습니다. componentDidUpdate()
componentWillUnMount()
가 라이프사이클의 역할을 한다.컴포넌트의 인스턴스가 생성되어, DOM에 삽입될 때 순서대로 호출된다.
1. constructor():
this.props
, this.state
에 접근할 수 있으며, 리액트 요소를 반환한다.2. getDerivedStateFromProps():
3. render():
4. componentDidMount():
Props나 state가 변경되면 랜더가 진행되며 순서대로 호출된다.
Key Ponint 💡 주의할 것은 업데이트 부분
1. props가 바뀔 때
2. state가 바뀔 때
3. 부모 컴포넌트가 리랜더링 될 때
4. this.foreceUpdate로 강제로 랜더링을 트리거할 때
1. getDerivedStateFromProps():
2. shouldComponentUpdate():
3. render():
4. getSnapshotBeforeUpdate():
5. componentDidUpdate():
1. componentWillUnmount():
Key Ponint 💡 리액트의 Hook은 함수형 컴포넌트에서 React state와 생명주기 기능을 연동(hook inito)할 수 있게 해주는 함수이다.
리액트 훅을 도입하게 된 목적은 여러가지가 존재한다.
컴포넌트에서 상태관련 로직을 사용할 때, Hook 이전에 재사용 가능한 로직을 사용하기 위해서는, render, props나 고차 컴포넌트와 같은 패턴을 사용했는데, 이런 패턴은 코드의 추적을 어렵게 만든다.
Hook을 활용하면, 상태 관련 로직을 추상화해 독립적인 테스트와 재사용이 가능해 레이어 변화 없이 재사용할 수 있다.
두 뻔째 목적으 기존의 라이프사이클 메서드 기반이 아닌, 로직 기반으로 나눌 수 있어서 컴포넌트를 함수 단위로 잘게 쪼갤 수 있다.는 점에서 이점이 있다.
1. 최상위에서만 Hook을 호출해야한다. (반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안된다.)
2. 리액트 함수 컴포넌트에서만 Hook을 호출해야하고, 일반 JS함수에서는 Hook을 호출해서는 안된다.
✨ Hook 종류
1. 기본 Hook
useState, useEffect, useContext
2. 추가 Hooks
useReducer, useCallback, useMemo, useRef, useImperativeHandle, useLayoutEffect, useDebugValue
Key Ponint 💡
동적으로 바뀌는 값을 관리할 때 사용하며, 상태 유지 값과 그 값을 갱신하는 함수를 반환한다.
const [state, setState] = useState(initialState);
인자(initialState)
의 값과 같다.setState(newState);
Key Ponint 💡
리액트 컴포넌트가 랜더링 될때마다 특정 작업(effect)을 실행할 수 있도록 하는 Hook component가 mount, update, unmount됐을 때 작업을 처리할 수 있다.
1) 클래스형 컴포넌트에서는 생명주기 메소드를 사용할 수 있었는데, 이를 함수형 컴포넌트에서도 사용할 수 있게 되었다.
2) 즉, 라이프사이클 훅을 대체할 수 있게 되었다. (componentDidMount, componentDidUpdate, componentWillUnmount)
useEffect는 `useEffect(callbackFunc, dependecies)로 두개의 인자를 넣어 호출할 수 있다.
1. useEffect(callBackFunc);
2. useEffect(callBackFunc, []);
3. useEffect(callBackFunc, [state1, state2]);
4. useEffect(()=>{ return(() => func()) });
① 의 경우 랜더링이 될 때마다 (컴포넌트가 마운트 된 후, 컴포넌트가 업데이트되고 난 후, 컴포넌트가 언마운트 되기 전에) 실행이 된다.
② depencies에 []
를 넣어주면, 컴포넌트가 최초 랜더링되었을 때만 실행이 된다.
③ 최초 랜더링 + state1 또는 state2가 변경이 되었을 때, 실행이 된다. 즉, 생명주기 메소드의 componentDidUpdate
, getDerivedStateFromProps
의 역할을 수행한다.
④ clean-up함수로 useEffect안에서 return 할 때 실행된다. 만약, 컴포넌트가 언마운트 될 때 이벤트 리스너를 통해 이벤트를 추가했다면, 컴포넌트가 언마운트 될 때 이벤트를 삭제해주어야 한다.그렇지 않으면, 컴포넌트가 리랜더링 될 때마다 새로운 이벤트 리스너가 핸들러에 바인딩 될 수 있다. 이는, 자주 리랜더링 될 경우 메모리 누수가 발생할 수 있다.
useEffect(()=>{
console.log("useEffect!!");
return () => {console.log("cleanup!!")};
})
console.log("useEffect!!")
을 수행하고, 컴포넌트가 unmount
될 때, console.log("useEffect!!")
와 console.log("cleanup!!")
를 수행한다.Key Ponint 💡 effect 타이밍
useEffect로 전달된 함수는컴포넌트 렌더링 - 화면 업데이트 - useEffect실행
순으로 실행이 된다. 즉, useEffect실행이 최초 렌더링 이후에 된다.
Key Ponint 💡 화면을 다 그리기 이전에 동기화되어야 할 때,
useLayoutEffect()
를 활용하여컴포넌트 랜더링 - useLayoutEffect 실행 - 화면 업데이트
순으로 effect를 실행할 수 있다.
Key Ponint 💡 React에서 DOM요소를 가져올 때 사용한다.
useRef는 리랜더링을 하지 않는다. 컴포넌트의 속성만 조회&수정 한다. 불필요한 리랜더링 방지를 위해서 useRef를 사용한다.
function App() {
const [name, setName] = useState("")
const ref = useRef("")
const handleName = () => {
setCurrentName(ref.current.value)
}
return (
<>
<input ref={ref} />
<button onClick={handleName}>제출</button>
</>
)
}
export default App
ref.current.value
를 통해 input에 있는 현재 value 값을 가져와서 제출하도록 한다. 실행하면, 화면을 처음 받아올 때와 제출 버튼을 눌렀을 때만 redering이 일어나게 된다. Key Ponint 💡 useCallback은 메모이제이션하기 위해서 사용되는 hook함수이다. 첫번째 인자로 넘어온 함수, 두번째 인자로 넘어온 배열 내의 값이 변경될 떄까지 저장하고 재사용할 수 있게 해준다.
useCallback
은 변수가 선언되어지면 해당 함수가 실행되어진다. 그 후에 deps의 변경을 통해 값이 변경이 되면 새로운 함수를 return하고, 값이 변경이 되어지지 않는다면 기존 함수를 return한다.const memizedCallback = useCallback(함수, 배열)
//App.js
import React, { useState, useCallback } from "react"
import List from "./List"
function App() {
const [number, setNumber] = useState(1)
const [dark, setDark] = useState(false)
const getItems = useCallback(() => {
return [number, number + 1, number + 2]
}, [number])
const theme = {
backgroundColor: dark ? "#333" : "#fff",
color: dark ? "#fff" : "#333",
}
return (
<div style={theme}>
<input
type="number"
value={number}
onChange={e => setNumber(parseInt(e.target.value))}
/>
<button onClick={() => setDark(prevDark => !prevDark)}>테마 변경</button>
<List getItems={getItems} />
</div>
)
}
export default App
Key Ponint 💡 성능 최적화를 위하여 연산된 값을 useMemo Hook을 사용하여 재사용하는 방법이다. 기존에 수행한 연산의 결과값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용한다.
- 이러한 메모이제이션을 통해서 중복 연산을 피할 수 있기 때문에 메모리를 조금 쓰면서 성능의 최적화를 할 수 있다.