원문링크 : Build own your react
원문을 보시면 글의 흐름에 따라 추가되고, 수정되는 코드를 한 눈에 확인할 수 있습니다.

미흡한 실력으로 읽어 내려가고, 실습해보면서 이해 안 가는 부분은 우선 넘어가고,학습 보조 도구로 작성한 글이라 해당 글에 대한 정보를 얻기에는 부족한 글입니다.

들어가기 전에

  • 왜? 자바스크립트로 리액트의 원리를 알고 싶어서
  • 결과물? 자바스크립트로 작성된 리액트 코드
  • 어떻게 ? ‘Build your own React’를 보고 따라한다, 더 알고싶은 부분은 공부해서 정리한다

💡목차
Step I: The `createElement` Function
Step II: The `render` Function
Step II: Concurrent Mode
Step IV: Fibers
Step V: Render and Commit Phases
Step VI: Reconciliation
Step VII: Function Components
Step VIII: Hooks

1. createElement Function

JSX —babel—> React.createElement

React의 element는 React.createElement 함수를 이용해 만들어지지만 가독성이 떨어지기 때문에 JSX 문법을 사용한다. JSX 문법은 babel이 JS로 변환한다.

React.createElement

function createElement(type,props,...children){ // (type,props,[children])
    return {
        type,// tag name
        props:{
            ...props,
            children
        }
    }
}
// text를 처리하기 위한 함수
function createTextElement(text) {
  return {
    type: 'TEXT_ELEMENT',
    props: {
      nodeValue: text,
      children: []
    }
  };
}

💡 뜬금없이 스프레드 연산자!

...children은 children이라는 배열의 요소를 스프레드 한 것. 따라서 type,props,[children]이 됨

2. render Function

ReactDOM.render

  1. DOM node 생성
  2. props 추가
  3. 생성한 node를 container에 추가
function render(element,container){
    const dom = 
                element.type == "TEXT_ELEMENT"
                ? document.createTextNode("") // text node
                : document.createElement(element.type) // element type으로 DOM노드 생성하기

  const isProperty = key => key !== 'children'
  // element에 props 추가
    Object.keys(element.props)
            .filter(isProperty)
            .forEach(name => {
                dom[name] = element.props[name]
            })                
    // children을 재귀적으로 
    element.props.children.forEach(child=>
        render(child,dom)    
    )
    container.appendChild(dom) // 생성한 node container에 추가
}

3. Concurrent Mode(동시성 모드)

render 함수에서 재귀호출부분의 경우 트리 끝까지 렌더가 될 때까지 동작을 멈출 수가 없다. 트리가 커지는 경우 긴 시간 동안 메인 스레드가 멈추게 된다. 브라우저가 높은 우선 순위의 작업을 처리하기 위해서 렌더를 멈출 필요가 있다.

function render(element, container) {
... 
 element.props.children.forEach((child) => render(child, dom)); 

  container.appendChild(dom);
}

그래서 작업을 작은 단위로 나누고, 각 단위가 끝나면 브라우저가 렌더링을 중단하도록 한다.

let nextUnitOfWork = null

function workLoop(deadline) {
  let shouldYield = false
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
    shouldYield = deadline.timeRemaining() < 1
  }
  requestIdleCallback(workLoop)
}

requestIdleCallback(workLoop)

function performUnitOfWork(nextUnitOfWork) {
  // TODO
}

requestIdleCallback : loop를 만들기 위해 requestIdleCallback를 사용한다. 브라우저의 메인 스레드가 idle 상태가 되면 requestIdleCallback을 호출한다.

4. Fibers

fibertree

각 작업 단위를 구조화 하기 위해 fiber tree 라는 자료 구조가 필요하다.

위와 같은 작업을 수행한다고 할 때 render 함수 내부에서 root fiber를 만들고, 이것을 nextUnitOfWork로 설정한다. 나머지 작업들은 performUnitOfWork function에서 일어난다.

각 fiber는 세가지 일을 한다.

  1. element를 DOM에 추가한다
  2. element의 children에 추가할 fiber를 만든다
  3. 다음 작업 단위를 선택한다.

fiber - children - ling - parent

fiber tree의 목적은 다음 작업단위를 쉽게 찾는데에 있다.

한 fiber의 작업이 끝났을 때, children이 있을 경우에는 children이 없을 때는 sibling이 다음 작업 단위가 된다.

sibling도 없을 경우 parent로 올라가고, root로 갈때까지 render 된다.

5. Render and Commit

지금의 코드에서 새로운 node가 DOM에 추가될 때 브라우저는 렌더 트리가 완성되기 전에 작업을 중단할 수 있다. 이 경우 우리는 미완성된 UI를 보게된다.

이를 위해 DOM을 제거하는 로직 대신, fiber 루트를 추적할 것이다. 이를 wipRoot(work in progress rot)라고 한다.

일단 모든 작업이 끝나고 나면 (더 이상 다음 작업이 없는 경우), 전체 fiber 트리를 돔에 커밋한다.

이 과정은 commitRoot 함수에서 이루어집니다. 여기서 모든 노드를 재귀적으로 dom에 추가한다.

6. Reconciliation(재조정)

node의 update와 delete는 render 함수로 얻은 element를 마지막으로 커밋한 fiber 트리와 비교한다.

이를 위해 커밋을 한 후 마지막 fiber tree를 저장한다. 이를 current root라고 부른다.

그리고 모든 fiber 속성에 alternate를 추가한다. 이것은 이전의 단계에서 커밋한 fiber다.

그런 다음 performUnitOfWork에서 새로운 fiber를 생성하는 로직을 추출해 reconcileChildren 함수를 만든다. 예전 fiber의 자식들과 재조정해야하는 element를 비교하기 위에 동시에 순회한다.

element와 oldFiber의 type 비교

  • 타입이 같으면 props만 바꾼다 - update
  • 타입이 다르면 새로운 element이므로 새로운 DOM node를 생성한다 - placement
  • 타입이 다르고 예전 fiber가 있으면 예전 node를 지운다 - deletion

effectTag 속성을 추가한다.

제거하고 싶은 노드를 추적하기 위해 delection 배열을 만든다.

commitRoot에서 delection 배열을 순회해 commitWork를 실행한다. commitWork 함수에서 effectTag를 이용해 append하거나 removeChild하거나 update한다.

갱신을 위해 updateDom 함수를 만든다.

예전 fiber의 props를 새로운 fiber의 props와 비교해 사라진 props는 제거하고, 달라진 props를 설정한다. 이 때 on으로 시작하는 props(event)가 있다면 eventListner를 추가한다.

7. 함수형 컴포넌트

함수형 컴포넌트로 만들어진 fiber에는 DOM node가 없다. 그래서 children을 props로 가져오는 대신 함수를 호출한다.

fiber 타입이 함수인지 체크한다음 updateFunctionComponent 에서 지금까지와 같은 역할을 하게끔 수정한다.

8. Hook

함수형 컴포넌트에 상태를 추가하기.

const Myact = {
  createElement,
  render,
  useState,
}

/** @jsx Didact.createElement */
function Counter() {
  const [state, setState] = Didact.useState(1)
  return (
    <h1 onClick={() => setState(c => c + 1)}>
    Count: {state}
    </h1>
    )
}
const element = <Counter />
const container = document.getElementById("root")
Didact.render(element, container)
let wipFiber = null
let hookIndex = null

function updateFunctionComponent(fiber) {
  wipFiber = fiber
  hookIndex = 0
  wipFiber.hooks = []
  const children = [fiber.type(fiber.props)]
  reconcileChildren(fiber, children)
}

함수형 컴포넌트를 호출하기 전에 useState 함수의 내부에서 사용하기 위한 몇몇 전역 변수들을 초기화해야 한다.

먼저 작업중인 fiber를 설정한다. 또한 그 fiber에 hook 배열을 추가함으로서 동일한 컴포넌트에서 여러 번 useState 함수를 호출 할 수 있도록 한다.

function useState(initial) {
  const oldHook =
        wipFiber.alternate &&
        wipFiber.alternate.hooks &&
        wipFiber.alternate.hooks[hookIndex]
  const hook = {
    state: oldHook ? oldHook.state : initial,
  }

  wipFiber.hooks.push(hook)
  hookIndex++
  return [hook.state]
}

함수형 컴포넌트가 useState를 호출할 때 이것이 오래된 hook인지를 체크하는데, 이때 훅 인덱스를 사용해 fiber의 alternate를 체크한다.

만약 우리가 가지고 있는 것이 오래된 hook이라면 상태를 초기화하지 않았을 경우 이 훅의 상태를 새로운 훅으로 복사한다.

그리고 새로운 훅을 fiber에 추가한 뒤 훅 인덱스 값을 증가시킨 다음 state를 반환한다.

const hook = {
  state: oldHook ? oldHook.state : initial,
  queue: [],
}

    const setState = action => {
      hook.queue.push(action)
      wipRoot = {
        dom: currentRoot.dom,
        props: currentRoot.props,
        alternate: currentRoot,
      }
      nextUnitOfWork = wipRoot
      deletions = []
    }

wipFiber.hooks.push(hook)
hookIndex++
return [hook.state, setState]
}

또한 useState는 상태를 갱신하는 함수 역시 리턴해야 하므로, 액션을 받는 setState 함수를 정의한다. 이 액션을 우리가 훅에 추가한 큐에 넣는다.

그리고 렌더 함수에서 했던 것과 비슷한 작업을 하는데, 새로운 작업중(wip)인 루트를 다음 작업할 단위로 설정하여 반복문에서 새로운 렌더 단계를 시작할 수 있도록 한다.

const actions = oldHook ? oldHook.queue : []
actions.forEach(action => {
  hook.state = action(hook.state)
})

아직 액션을 실행하지는 않았다. 이는 컴포넌트 렌더링 다음에 수행하는데, 오래된 훅의 큐에서 모든 액션을 가져온 다음 이를 새로운 훅 state에 하나씩 적용하면 갱신된 state를 얻을 수 있게 된다.

새로 알게된 것

  • React의 element는 React.createElement 함수를 이용해 만들어지지만 가독성이 떨어지기 때문에 JSX 문법을 사용한다. JSX 문법은 babel이 JS로 변환한다.
  • react는 작업단위를 구조화하기 위해 fiber tree 구조를 사용한다.
  • virture DOM은 render 함수로 얻은 element 즉, javascript 객체다. 리액트는 이전의 tree와 새로운 tree를 동시에 순회해 비교한다.

'React' 카테고리의 다른 글

리액트에서 Variable과 State의 차이가 뭘까?  (0) 2022.01.13
presentational + container 패턴  (0) 2021.10.30
React Query  (0) 2021.07.09

이게 왜 궁금해?

data.index.status === Status.Approval

or

const isAppoval = data.index.status === Status.Approval;

이라는 조건을 여러 곳에서 반복적으로 사용하게 되었다.

딱 봐도 길고, 반복적으로 사용하고, 여러 조건들과 함께 쓰일 경우에는 더 의미를 직관적으로 알 수 없었다.

그래서 isApproval 이라는 무언가로 만들어 쓰려고 하는데 data가 변경될 때마다 isApproval 상태도 변경되어야 했다.

그래서 그냥 변수(variable)로 선언해도 될지, setState는 사용하지 않지만, state로 선언해야할지 궁금해서 찾아보았다.

결론부터

class variable은 변경했을 때 React가 알아채지 못한다.

state는 setState로 변경했을 때 react가 변화를 감지해 re-rendering된다.

내 코드의 경우 isApproval을 내가 직접 변경하지 않고, data에 따라 변하기 때문에 변화를 감지해 리렌더링 시켜야할 일은 없다. data가 변경되었을 때 렌더링되면서 isApproval의 값도 바뀌므로 변수로 사용해도 됨!

🤷‍♀️State가 뭐지?

// class variable
export default class Test extends Component {
  render(props) {
        super(props);
    const isMe = this.props.name === 'Gisele';
    return (
      <div>
        {isMe ? '나다' : '나 아니다'}
      </div>
    );
  }
}

// state
export default class Test extends Component {
  constructor(props) {
        super(props)
    this.state = { isMe : false };
  }

  render() {
    if(this.props.name === 'Gisele') {
      this.setState({isMe: true});
    }
    return (
      <div>
        {isMe ? '나다' : '나 아니다'}
      </div>
    );
  }
}

state와 variable의 차이는 class형 컴포넌트에서 더 알기 쉬워서 가져옴.

State 는 컴포넌트의 현재 상황에 대한 정보를 나타내는 일반 자바스크립트 객체다(클래스형 컴포넌트에서). 일반 변수는 함수가 종료될 때 사라지지만, state는 값이 보존된다.

컴포넌트의 역할은 raw data를 HTML로 변환하는 것이기 때문에 이 raw data는 props와 state 객체로 구성되어 있다고 생각할 수 있다. props와 state가 render() 함수의 입력 데이터라고 할 수도 있다. 따라서 state는 구성 요소의 동작을 제어하는 일부 속성을 나타낸다.

state는 상태 변수에 변경 사항을 직접 적용하지 않는다. 이는 React에서 변경사항으로 간주되지 않기 때문이다. 대신 state의 상태는 setState() 를 사용해 변경한다. 그리고 setState는 비동기식이다. 한 줄에서 setState를 호출하고 다음 줄에서 상태가 업데이트가 된다는 보장은 없다. setState가 상태를 업데이트하라는 명령이 아니고 상태 업데이트에 대한 요청이기 때문이다. 이 문제를 해결하지 위해 업데이트가 적용된 후 실행되도록 보장되는 setState() 콜백 함수를 사용할 수 있다.

함수형 컴포넌트에서는 useState() 훅을 사용해 state를 관리한다. 클래스형 컴포넌트에서 state는 항상 객체지만, 함수형 컴포넌트에서는 모든 type이 state가 될 수 있다. 각 상태는 단일 값을 보유한다.

초기 값은 초기 렌더링에만 할당된다. 이후 렌더링에서 useState의 인수는 무시되고 현재 값이 검색된 값이 된다.

const Avartar = (user) => {
    const [userState, setUserState] = useState(user);

    // 해결
    useEffect(()=>{
        setUserState(user)
    },[user])

    return user.avartar && (<img src={user.avartar}>)
}

useState만 사용하면 해당 인수가 prop이 변경될 때마다가 아니라 처음에만 사용되기 때문에 작동하지 않는다. 이 문제를 해결하려면 들어오는 객체를 state로 사용할 때는 새 객체를 만들어야 한다.

그렇지 않으면 리액트는 객체의 변경을 감지하지 못한다.(객체는 참조형 data이기 때문에 객체 내부 값이 변해도 객체 자체의 주소값이 변하기 않기 때문)

const Message = ()=>{
    const [messageObj,setMessageObj] = useState({message:''})

    return (
        <>
           <input type='text'
                  value={messageObj.message}
                  onChange={e=>{
                  // 동작하지 않는 예시
                     messageObj.message = {e.target.value};
                     setMessage(messageObj) 

                 // 동작하려면 새로운 객체를 만들어야 한다.
                      const new = {message:e.target.value};
                      setMessage(newMessageObj)

                 // 또는 setState의 첫 번째 인수인 prevState와 스프레드를 이용할 수 있다
                       setMessage(prevState=>{
                           return {...prevState,message:e.target.value}
                       })

                }} />
        </>
    )
}

위 내용으로 state의 특징을 정리하면 다음과 같다.

  1. state는 컴포넌트의 어떤 정보를 갖고 있는 데이터다.
  2. state가 변경되면 React는 이를 감지해 render가 일어난다.
  3. state는 바로 할당하는 방식으로 변경하면 react가 변화를 감지하지 못하므로 setState를 이용해 변경한다.
  4. setState는 비동기로 처리되므로, setState 바로 다음 명령에서 새로운 값이 반영되지 않을 수 있음
  5. state를 객체로 사용할 경우 값을 할당할 때 스프레드 연산자 등을 이용해 새 객체를 만들어줘야함(객체의 data type이 참조형이기 때문에 내부값이 변해도 객체 자체의 주소값이 변하지 않기때문)
  6. setState는 첫 번째 인자로 이전 상태값을 갖는다.

그리고 내가 예전에 몰랐거나 실수를 범했던 것들의 이유를 알 수 있었다.

❓ 이 함수에서 setState를 여러번하면 렌더링이 매번 일어나나? -> 아니다. 해당 블럭이 모두 실행된 후! 렌더링이 일어난다.
❓ 객체 state가 왜 안바뀌었지? 새로운 객체를 생성하지 않고, 객체에 값을 바로 할당했기 때문
❓ 바로 위에서 setState를 했는데 왜 이전 상태값으로 처리가 됐지? 같은 함수 블럭 안에서 setState한 직후 실행한 것은 아직 state에 새로운 값이 반영되기 전이므로 그렇다. 코드 블럭이 다 실행된 후 state에 값이 반영된다.

State의 불변성


referece

'React' 카테고리의 다른 글

나만의 React 라이브러리 만들기  (0) 2022.01.18
presentational + container 패턴  (0) 2021.10.30
React Query  (0) 2021.07.09

회사에서 Presentational&container 패턴을 적용하게 되어, 정확한 개념을 공부하고, 공부하면서 알게된 사실을 정리한 글입니다.

Presentaional&container 패턴이란?

개념

  • Hook이 존재하기 전에 컴포넌트 재사용성을 높이기 위해 컴포넌트 내에서 추가적으로 레이어를 나눠 컴포넌트 간 의존도를 낮추기 위한 방법으로 등장했다.
  • 즉, 로직을 수행하는 컴포넌트와 UI를 보여주는 컴포넌트가 분리된 패턴이다.

Presentational component

  • 사용자가 직접 보고, 조작하는 컴포넌트
  • stateless : state를 직접 조작하지 않고 container component가 내려준 prop의 함수에 의해 state를 변경한다.
  • 작은 레고 블럭처럼 가능한 작게 만들어야 한다.
  • 상태를 거의 가지지 않으며, UI에 관련된 상태만 가질 수 있다.

Container component
- 어떠한 동작을 할 것인가에 대해 책임진다.
- statefull : 주로 상태를 가지고 있다.
- side effects를 만들 수 있다.
- 절대로 DOM 마크업 구조나 스타일을 가져서는 안된다.

이점

  1. presentational component의 재사용성이 뛰어나다.
    로직이 포함되어 있지 않기 때문에 다른 컴포넌트와의 의존도가 낮아 다양한 container와 조합해 재사용하기 쉽다.
  2. 앱의 기능과 UI에 대한 구분이 쉬워지므로 구조에 대한 이해가 쉬워진다.
  3. 반복되는 마크업 작업을 줄일 수 있다.
  4. 같은 state를 다른 container에게 props로 전달함으로써 상태를 공유할 수 있다.

코드예시

사용하지 않는 걸 추천합니다


이 패턴의 창시자인 Dan Abramov님이 2019년에 더 이상 이렇게 구성 요소를 분할하지 않는 것이 좋다는 말을 남기셨다. 잘 쓰고 있어서 찬양하려고 글을 쓰는 중이었는데..?

코드베이스에서 자연스럽다면 이 패턴이 유용할 수도 있지만, Hooks를 사용하면 임의의 분할 없이 동일한 작업(Hook은 로직과 표현의 분리를 가능하게 한다)을 수행할 수 있고, 필요에 의해서가 아닌 맹목적으로 사용하는 경우를 많이봤기 때문이라고 한다.

여전히 중요하다고요?

하지만 2021년에도 해당패턴이 중요한 이유를 설명한 글을 찾았다!

컴포넌트 안에서 데이터를 처리하는 로직을 사용하는 경우 컴포넌트의 재사용성이 떨어지는 것은 사실이다. 특히 특정 라이브러리와 프로그램의 아키텍처에 강하게 의존성이 생긴다. 예를 들어 react-intl 라이브러리에서 제공하는 hook을 쓰다가, react-i18n으로 라이브러리를 바꾼다면? 라이브러리리에서 제공하는 로직을 사용하는 모든 컴포넌트를 찾아 수정해야할 것이다.

presentatation 계층의 컴포넌트라면 스타일이나 Bootstrap, Material UI와 같은 라이브러리와 연결하는 것이 좋다.

Presentainal Component

  1. html, css 및 기타 프리젠테이션 구성요소만 사용할 수 있다.
  2. 다른 App에서도 사용할 수 있어야 한다. (App의 설계나 framework에 종속되지 않음)
  3. style 관련된 hook만 포함해야 한다.
  4. 고차 컴포넌트로 래핑되지 않는다. 예를 들어 리덕스를 사용한다면, 더 높은 순서의 연결 컴포넌트가 컨테이너 컴포넌트에 추가되어야 한다.
  5. 컴포넌트의 시각적 특성을 변경하는 props를 종종 허용한다.
  6. 완전히 다른 스타일 시트를 로드하는 props를 종종 허용한다.

Container Component

  1. html이나 style에 관한 것은 포함하지 않는다.
  2. presentation component나 다른 container component를 사용할 수 있다.
  3. 로직은 hook file에 들어간다. (reacdt-hooks-testing-library를 사용해 test하기 위함)
  4. Props를 받을 수 있다.
  5. Context를 사용하고, side effect를 만들고, db에 CRUD 작업을 요청할 수 있다.
  6. CRUD 작업이 성공했을 때 UI에 즉시 반영하고, 실패했을 때는 UI를 롤백한다.
  7. 항상 cotnainer 구성 요소에서 테스트 ID를 선언하고 구성 요소에 테스트 ID를 전달한다.

정리

회사에서도 함수형 컴포넌트와 Hook을 사용했고, container도 unstated-next가 제공하는 hook을 이용해 container를 만들었다.
개념적으로 view와 데이터 관련 로직을 분리했지만, container component가 아닌 context에 관련 로직을 작성했다.

그렇게 함으로써 컴포넌트의 재사용성이 높아졌고, 불필요한 컴포넌트의 수를 줄일 수 있었다. 예를 들어 영화목록 페이지라고 하면
Movie Page > Movie Table 컴포넌트 = 데이터 로직 + table 컴포넌트 로직 으로 구성했다면
Movie Page + Container --props--> table 컴포넌트 로 구성을 할 수 있다.

그리고 구조적으로도 파악하기 쉽고 presentatonal component가 깔끔해졌다.
Container가 복잡해지는 문제가 있지만 관심사에 따라 Container를 분리하면 될 것 같다.

이번 내용을 정리하면서 Presentational - container pattern의 개념과 등장배경에 대해 알 수 있었고, Hook과 ContextAPI에 대해 더 공부해야겠다고 느꼈다.


reference

'React' 카테고리의 다른 글

나만의 React 라이브러리 만들기  (0) 2022.01.18
리액트에서 Variable과 State의 차이가 뭘까?  (0) 2022.01.13
React Query  (0) 2021.07.09

React 애플리케이션에서 서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트 하는 작업을 쉽게 만든다.

// ✅ : react-query를 사용하면 하나의 훅으로 모든 페칭에 연관된 상태를 한번에 제어할 수 있다.
const { status, data, error, isFetching } = useQuery(() => fetch(URL));

핵심개념 : Server-state와 client-state -- global state ㄴㄴ

  • 비동기 관련된 로직 처리 -> 동기적으로 업데이트 되는 데이터와 액션만 남길 수 있다.
  • Redux를 더 취지에 맞게 사용할 수 있음 : Redux는 전역관리를 useQuery는 서버에서 받아온 데이터 관리를

// TODO : 우리 프로젝트에 react-query를 쓰려는 이유가 뭐지? 상태관리를 리덕스없이 진챠 할 수 있는 것?

만들어진 동기

  • react 자체가 데이터를 패칭해오거나 업데이트 하는 옵셔을 제공하지 앟ㄴ기 때문에 원래 React 개발자들은 각자의 방식으로 http 통신 로직을 짜야했다.
  • Redux 같은 전역 상태관리 라이브러리들이 클라이언트 상태값에 대해서는 잘 작동하지만, 서버 상태에 대해서는 그렇게 잘 작동하지 않는다.Server State는 Client State와 완ㄴ전 다르기 때문
    • 서버 데이터는 항상 최신 상태임을 보장하지 않는다. fetching을 수행해야만 최신 데이터로 전환됨
    • 네트워크 통신은 최소한으로 줄이는게 좋은데, 복수의 컴포넌트에서 최신 데이터를 받아오기 위해 fetching을 여러번 수행하는 낭비가 발생할 수 있다.

사용법

  • App.js에서 Context Provider로 컴포넌트를 감싸고 queryClient를 내려보내줌
    // TODO : Context가 뭐임??/

컨셉

  • Query들은 4개의 상태를 가지며, useQuery가 반환하는 객체의 프로퍼티로 어떤 상태인지 확인이 가능하다
    • fresh : 새롭게 추가된 쿼리 인스턴스
    • fetching : 요청을 수행 중인 쿼리
    • stale : 인스턴스가 존재하지만 이미 패칭이 완료된 쿼리. 특정 쿼리가 stale된 상태에서 같은 쿼리를 useQuery로 또 호출해 마운트를 시도하면 캐싱된 데이터 반환
    • inactive : active 인스턴스가 하나도 없는 쿼리. 렌더링시에 다시 호출되지 않은 쿼리들은 inactive됨
  • refetching이 일어나는 경우?
    • 렌더링 간 쿼리 인스턴스가 다시 만들어졌을 때
    • window가 다시 포커스 되었을 때
    • 네트워크가 다시 연결되었을 때
    • refetch interval이 있을 때 : 요청 실패한 쿼리는 디폴트로 3번 더 백그라운드단에서 요청하며, retry, retryDelay 옵션으로 간격과 횟수를 커스텀 가능하다

// TODO : 쿼리 인스턴스가 뭐임?

useQuery

useMutation

  • server state에 side effect를 일으키는 경우에 사용(create, update, delete)
  • useQuery를 호출하면 최상위에서 호출해야한다는 훅의 규칙에 위배되기 때문인듯

📑 reference
김맥스 기술 블로그 - React-Query 살펴보기

+ Recent posts