cause Node Version Upgrade

solve: 

react-scripts --openssl-legacy-provider start

'web > React' 카테고리의 다른 글

React-Query Dynamic Parallel Queries  (0) 2023.06.06
React Context API  (0) 2023.04.01
제너레이터(Generator)  (1) 2021.01.11
Redux 비동기(redux-saga)  (0) 2021.01.11
리액트 내장 훅  (0) 2021.01.01

병렬 쿼리에 대한 게시글을 찾아보던 도중,  

 

If the number of queries you need to execute is changing from render to render, you cannot use manual querying since that would violate the rules of hooks.

 

어떤 게시글에서 위 글의 `violate the rules of hooks` 가 'Hooks 의 어떤 규칙을 위반한 것인지는 모르겠지만' 이라고 포스팅을 해두었길래 무엇을 의미하는 것인지 적어본다.

 

React 의 가장 중요한 기초 개념 중에 하나가

'Hook 은 최상위 레벨에서 동작해야한다' 이다.

 

'If the number of queries you need to execute is changing from render to render'

렌더마다 실행해야하는 쿼리(Hooks)의 수가 변경된다면

'you cannot use manual querying'

수동으로 쿼리를 작성할 수 없다

 

쉽게 이해해보자면,

Hooks 를 숫자타입을 가지는 상태값에 따라 조건적으로 실행할 수 없다는 뜻으로 설명할 수 있다

방금한 설명을 코드로 표현해본다면

 

const [cnt, setCnt] = useState<number>(...);
const [value, setValue] = useState<any>();

if (cnt == 1) {
	useEffect(() => {
    	(async() => {
        	const response = await fetch(...);
            setValue(response.data);
        })()
    }, [])
}

...

return <... />

 

위 코드의 문제점이 무엇일까

cnt 값에 따라 if 문 내에서 실행되는 useEffect 는 과연 정상적으로 실행될까?

 

정답은 당연히 x 이다.

React 의 상태관리 메커니즘은 실행될 Hooks 의 순서를 기억한다.

if 문 등의 특정 '조건' 이 상태관리 메커니즘에 저장된 순서를 동적으로 변경할 수 없다는 의미가 된다.

 

React 의 기초개념 'Hook 은 최상위 레벨에서 동작해야한다' 의 가 위 설명을 포함하고 있다고 보면 된다. 

'web > React' 카테고리의 다른 글

digital envelope routines::unsupported  (0) 2023.06.20
React Context API  (0) 2023.04.01
제너레이터(Generator)  (1) 2021.01.11
Redux 비동기(redux-saga)  (0) 2021.01.11
리액트 내장 훅  (0) 2021.01.01

전역 상태가 아닌 특정 컴포넌트의 상태를 관리할 때는 context API를 많이 활용한다.

이를 좀 더 편하게 사용하기 위해 아래와 같은 형태로 context 를 만들어서 사용한다.

 

// context/management

import React, { useContext, useEffect } from "react"
import { SOME_TYPE } from "./type/management";

interface IManagementState {}
interface IManagementAction {
  type:string;
  payload?:any;
}

const initState:IManagementState = {};

export const managementReducer = (state:IManagementState, action:IManagementAction) => {
  switch(action.type) {
    case SOME_TYPE : {
      // do something
      // ..
      return state;
    }
    default:
      return state;
  }
}

const managementContext = () => {

  const ctx = React.createContext<{
    state:IManagementState
    dispatch:React.Dispatch<IManagementAction>
  }>({
    state:initState,
    dispatch:() => null
  })

  const useCtx = () => {
    const c = useContext(ctx);
    return c;
  }

  return [
    ctx.Provider,
    useCtx,
    initState
  ] as const;
}

export default managementContext;


// context/type/management

export const SOME_TYPE = 'SOME_TYPE';


// page

import managementContext, { managementReducer } from '@/context/state'
import { useReducer } from 'react';

export const [ManagementContextProvider, useManagementContext, initManagementContextState] = managementContext();

export default function Page() {

  const [state, dispatch] = useReducer(managementReducer, initManagementContextState);

  return (
    <ManagementContextProvider 
    value={{
      state, 
      dispatch
    }}>
      {/* ... */}
    </ManagementContextProvider>
  )
}

 

Context Provider 를 state, dispatch 2개 각각 만들어서 성능최적화를 시켜줄 수 있지만.. 패스

 

context 의 상태는 reducer 를,

useCtx 함수를 통해 useContext 객체를 외부로,

useReducer 를 통해 context reducer 와 context initState 를,

이를 Provider를 통해 Context API 를 사용할 수 있도록 하는 구조이다  

 

하위 컴포넌트에서는 아래와 같이 export 한 useContext 객체를 손쉽게 사용할 수 있다

 

const { state, dispatch } = useManagementContext()

 

최근에 서로 다른 페이지에서 동일한 형태의 뷰를 갖는 컴포넌트를 추가해야 했는데,

기존의 컴포넌트는 위의 구조로 상태관리를 하고 있었다

 

기존에 사용하던 view 단의 context 가 위와 같이 managementContext 였다면

사실 managementContext2 로 복사해서 사용하면 그만이지만..

또 언제 같거나 비슷한 형태의 뷰를 추가해야할지 모르기에

HOC 를 이용하면 아래와 같이 간단한 수정으로 컨텍스트의 중복 없이 같은 구조의 Context 를 사용할 수 있다

 

// hoc/management.tsx

import managementContext, { managementReducer } from "@/context/state";
import { PropsWithChildren, useReducer } from "react";

export const [ManagementContextProvider, useManagementContext, initManagementContextState] = managementContext();

const ManagementContextHOC = ({ children }:PropsWithChildren<{}>) => {

  const [state, dispatch] = useReducer(managementReducer, initManagementContextState);

  return (
    <ManagementContextProvider
    value={{state, dispatch}}>
      {children}
    </ManagementContextProvider>
  )
}

export default ManagementContextHOC;

 

// page1.tsx
import ManagementContextHOC from '@/hoc/management';

export default function Page1() {

  return (
    <ManagementContextHOC>
      <SomeComponent />
    </ManagementContextHOC>
  )
}

 

// page2.tsx
import ManagementContextHOC from '@/hoc/management';

export default function Page2() {

  return (
    <ManagementContextHOC>
      <SomeComponent />
    </ManagementContextHOC>
  )
}

 

context api + useReducer + HOC 를 이용한 위와 같은 구조는

무분별한 전역 상태 관리가 아닌

필요한 뷰 단에서의 상태관리를 구분짓기에 좋은 구조라고 생각한다

'web > React' 카테고리의 다른 글

digital envelope routines::unsupported  (0) 2023.06.20
React-Query Dynamic Parallel Queries  (0) 2023.06.06
제너레이터(Generator)  (1) 2021.01.11
Redux 비동기(redux-saga)  (0) 2021.01.11
리액트 내장 훅  (0) 2021.01.01

redux-saga을 사용하기 위해 기본이 되는 제너레이터에 대해서 알아보자

 

 

* 기호를 사용해서 제너레이터를 선언할 수 있다.

 

함수 f1은 제너레이터 함수이다.

 

제너레이터 함수를 호출하면 제너레이터 객체가 반환된다.

 

코드에서 보이는 로그는 실행과정을 표현하기 위해 작성한 것이다

 

제너레이터 객체에는 next()라는 메서드가 있다

 

위 코드에서 next()를 처음 실행하면, 로그를 출력하고 yield 10이 실행된다

 

반환 값을 보면 yield 옆의 10이라는 값이 value로 반환되는 것을 볼 수 있다

 

마지막 next()를 실행하면 return 옆의 finished가 value로 반환되는 것과, done이 true로 반환되는 것을 볼 수 있다

 

제너레이터를 제대로 이해하려면 iterator와 iterable을 이해해야 한다

 

iterator - 반복자

반복자 객체인 iterator는 다음과 같은 조건을 만족한다

- next 메서드를 가지고 있다

- next 메서드는 value와 done 속성값을 가진 객체를 반환한다

- done 의 속성값이 true일 때 작업이 끝났음을 의미한다

즉 위에서 본 제너레이터 객체갸 iterator 이다

 

iterable - 반복 가능 객체 

- Symbol.iterator 속성값으로 함수를 갖고 있다

- 해당 함수를 호출하면, 반복자 객체 iterator를 반환한다

 

위에서 만든 제너레이터 객체 gen으로 설명하면

console.log(gen[Symbol.iterator]() === gen); // true

제너레이터 객체에는 Symbol.iterator 라는 속성값이 있는데, 이 함수를 실행하면 자기 자신이 나오게 된다

 

그래서 제너레이터 객체는 iterator 이면서 iterable 이 된다

 

배열에는 iterator 가 있다는 것을 알고 있을 것이다

마찬가지로 배열처럼 선형 가능한 자료구조에는 iterator가 있다.

이런 자료구조에는 Symbol.iterator 라는 속성값이 있기 때문에 iterator 객체를 얻을 수 있고 next 를 호출할 수 있다

 

iterable을 만족하면 자바스크립트의 몇가지 기능을 사용할 수 있다

 

예를 들면 for - of 같은 메서드나, 전개연산자 ...iterator객체 와 같이 사용이 가능하다

 

제너레이터로 위와 같이 코드를 사용하는 것과

일반적인 코드로 위와 같은 코드를 사용하는 것은 매우 다르다

 

위를 콘솔로 찍어보면 아래와 같다

일반적인 코드로 위 반복문을 실행했다면 멈출 수 없는 무한루프 코드가 될 것이다

 

하지만 제너레이터는 위처럼 값을 하나씩 가지고 올 수 있다

 

이게 가능한 이유는 제너레이터는 '실행'을 멈출 수 있기 때문이다

 

실행을 멈추고 실행 권한을 외부에 위임한다는 의미이다

 

그리고 외부에서 어떤 신호가 왔을 때 다시 실행할 수 있다

 

generator에 대한 설명은 ko.javascript.info/generators

 

제너레이터

 

ko.javascript.info

여기에서 정말 자세하게 설명해주고 있기 때문에 한번 읽어보는 것을 추천한다. 아니 권장한다

'web > React' 카테고리의 다른 글

React-Query Dynamic Parallel Queries  (0) 2023.06.06
React Context API  (0) 2023.04.01
Redux 비동기(redux-saga)  (0) 2021.01.11
리액트 내장 훅  (0) 2021.01.01
visual studio Code 단축키/json  (0) 2020.12.31

+ Recent posts