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

파라미터 value의 경우 string 타입의 변수로만 사용가능하다.

파라미터의 타입을 string 뿐만 아니라 원하는 다른 타입으로 선언하고자 할 때는

대표적으로 any를 생각할 수 있지만, 좀 더 원하는 구체적인 타입을 지정해주고자 할 때는 | 연산자를 사용할 수 있다

이를 Union 타입이라고 하며, 하나 이상의 원하는 타입을 정해줄 수 있는 방법이다

 

유니온 뿐만 아니라 이러한 타입스크립트의 강점 중 하나는 타입 가드가 가능하다는 것

즉 특정 타입으로 타입의 범위를 좁혀나가 타입에 맞는 속성들을 바로바로 사용할 수 있다는 것이 편하다

 

인터페이스를 사용했을 때의 특징

여러 개의 인터페이스를 사용해서 매개변수로 사용할 때는 

A라는 변수가 특정 타입으로 확정되지 않은 이상

A가 접근할 수 있는 속성값은 유니온으로 설정한 모든 인터페이스에서 공통적으로 가진 속성만 뷰접근이 가능하다

타입스크립트가 변수의 속성값에 대해서 접근할 수 있는 프로퍼티는 모든 유니온을 만족시키는 공통된 프로퍼티이다

 

유니온 타입과 반대되는 연산자로 인터섹션 타입이 있다

인터섹션 타입은 & 연산자를 사용한다

유니온의 | 을 생각해본다면 인터섹션 타입은 어떤지 쉽게 예상할 수 있을 것 같다

변수 value의 뷰프로퍼티를 확인해보면 interface person과 user의 모든 속성을 사용할 수 있음을 확인할 수 있다

여기서 name 프로퍼티의 경우 공통된 속성이기 때문에 name으로 커서를 옮기면

name: string 이라고 표현되어 있고,

age로 커서를 옮기면 person.age : number을,

email로 커서를 옮기면 user.email : string으로

어떤 인터페이스의 속성과 타입인지 확인할 수 있다

 

유니온 타입과 인터섹션 타입은 함수를 호출할 때 약간의 차이가 있다

이 차이 때문에 주로 유니온 타입이 좀 더 선호되는 편이다

 

유니온 타입을 사용한 함수를 호출할 때는

유니온으로 선언한 타입 중 하나를 만족시켜서 호출하면 된다. 아래를 보자

 

인터섹션 타입을 사용한 함수를 호출할 때는

선언한 모든 타입을 만족시켜야 한다는 규칙이 있다

즉, person과 user를 인터섹션으로 사용한다면 호출할 떄 이에 필요한 모든 속성이 필요하게 된다

 

첫번째 호출은 age라는 속성을 사용하지 않았기 때문에 에러가 발생하는 것을 볼 수 있다

두번째 호출처럼 모든 속성을 만족시켜야 한다

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

Type Aliases  (0) 2021.01.13
Interface  (0) 2021.01.13

+ Recent posts