반응형

React.memo

React.memo는 Higher-Order Components(HOC)이다.

👀 Higher-Order Components(HOC)란 컴포넌트를 인자로 받아 새로운 컴포넌트롤 다시 return해주는 함수이다.

const NewComponent = higherOrderComponent(WrappedComponent);

일반 컴포넌트는 인자로 받은 props를 UI에 활용하는 반면에, higher-order component는 인자로 받은 컴포넌트를 새로운 별도의 컴포넌트로 만든다. HOC는 리액트의 API가 아니라 리액트가 컴포넌트를 구성하는데 있어서의 일종의 패턴이라고 보면된다.

 

React.memo의 사용법은 다음과 같다.

const MyComponent = React.memo((props) => {
	return (/*컴포넌트 렌더링 코드*/)}
);

만약 컴포넌트가 같은 props를 받을 때 같은 결과를 렌더링한다면 React.memo를 사용하여 불필요한 컴포넌트 렌더링을 방지할 수 있다.

즉, 컴포넌트에 같은 props가 들어온다면 리액트는 컴포넌트 렌더링 과정을 스킵하고 마지막에 렌더링된 결과를 재사용한다.

 

React.memo는 오직 props가 변경됐는지 아닌지만 체크한다. 만약 React.memo에 감싸진 함수형 컴포넌트가 함수 내부에서 useState나 useContext같은 훅을 사용하고 있다면, state나 context가 변경될 때마다 리렌더링된다.

 

기본적으로 props로 들어온 object는 shallow compare로 비교한다. 즉, props로 들어온 number, string과 같은 scarlar 값은 실제 값이 동일한가를 비교하지만, object의 경우 scarlar 값과 달리 같은 값을 'reference(참조)'하고 있는지를 비교 한다.

 

만약 비교방식을 커스텀하고 싶다면 아래 코드처럼 비교함수를 React.memo의 두번째 인자로 넣어주면 된다.

function MyComponent(props) {
  /* 컴포넌트 렌더링 코드 */
}
function areEqual(prevProps, nextProps) {
  /*
  만약 전달되는 nextProps가 prevProps와 같다면 true를 반환, 같지 않다면 false를 반환
  */
}

export default React.memo(MyComponent, areEqual);

 

useMemo

useMemo는 메모이즈된 값을 return하는 hook이다.

인자로 함수와 의존 값(dependencies)을 받는다. useMemo는 두번째 인자로 준 의존 인자 중에 하나라도 변경되면 값을 재 계산한다. 이를 통해 매 렌더시마다 소요되는 불필요한 계산을 피할 수 있다. 만약 dependencies 인자로 아무것도 전달되지 않는다면, 렌더시마다 항상 값을 새롭게 계산하여 return한다.

 

아래의 코드는 a, b값이 변할 때만 첫번째 인자로 들어온 함수가 실행되어 재계산이 되고, 그렇지 않은 경우에는 메모이즈된 값을 return한다.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

 

공통점

React.memo와 useMemo 모두 props가 변하지 않으면(이전 props와 동일하면) 인자로 넘긴 함수는 재실행되지 않고, 이전의 메모이즈된 결과를 반환한다는 점에서 공통점이있다. 

 

아래 React.memo와 useMemo를 사용한 코드를 보면 두가지 코드는 props.name의 값이 변하지 않는다면 리렌더링 되지 않고 이전의 값을 반환한다는 점에서 동일하게 동작한다.

/*별도로 두번째 인자를 넘기지 않을 경우 props가 변하지 않는다면 재렌더링 되지 않음*/
const NameTag = React.memo(
  (props) => <div>{props.name}</div>
);

/*만약 두번째 인자로 특정 props.name값이 같지 않을때만 재렌더링 하도록 커스텀 비교 함수를 넣어주고 싶을 때*/
const NameTag = React.memo(
  (props) => <div>{props.name}</div>
,
  (prevProps, nextProps) => prevProps.name === nextProps.name
)
function NameTag(props) {
  return useMemo(
    () => <div>{props.name}</div>
  ,
    [props.name]
  )
}

 

차이점

1. React.memo는 HOC, useMemo는 hook이다.

2. React.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만, useMemo는 hook이기 때문에 오직 함수형 컴포넌트 안에서만 사용 가능하다.

 

출처:https://sustainable-dev.tistory.com/137

반응형
반응형

이번 포스팅에서는 React에서 setState의 Callback 함수를 사용하는 경우를 소개합니다.

 


setState의 Callback 함수를 사용하는 경우

react에서 setState의 Callback 함수를 사용하는 경우는 state를 변경 후 변경된 state를 사용하는 경우입니다.

 

우선, Callback 함수를 사용하지 않은 경우 어떠한 문제가 발생하는지 알아봅시다.

 

다음은 [* 2] 버튼을 클릭하면 this.state.num의 값을 2배로 변경 후 콘솔에 this.state.num의 값을 출력하는 함수입니다.

twiceButtonClick = () => {
    this.setState({ num: this.state.num * 2 });
    console.log("state : " + this.state.num);
  };

실행 결과

[* 2] 버튼을 클릭 전 결과입니다.

[* 2] 버튼 클릭 전

[* 2] 버튼을 클릭 후 결과입니다.

[* 2] 버튼 클릭 후

setState 함수가 실행되어 this.state.num의 값이 2로 변경되었는데, 콘솔에는 변경되기 전 값이 출력되었습니다.

 

이러한 이유는 setState가 비동기 함수라서 그렇습니다.

 

setState 함수는 이벤트 핸들러 함수에서 바로 값을 갱신(변경)하는 것이 아니라 이벤트 핸들러 함수가 종료 후 react에 의해 state 값이 갱신됩니다.

 

setState 함수로 state의 값을 변경 후 갱신된 값을 콘솔에 출력하기 위해서는 setState의 Callback 함수를 사용해야 합니다.

 

Class 컴포넌트의 setState Callback

다음 코드는 setState 함수의 정의인데, 2개의 인자가 존재합니다.

setState<K extends keyof S>(
  state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
  callback?: () => void
): void;

첫 번째 인자는 변경할 state를 전달하며, 두 번째 인자는 state가 변경 후 실행되는 callback 함수를 전달합니다. 

 

즉, state의 값을 변경 후 특정한 기능 또는 동작이 실행되도록 하고 싶다면, setState의 두 번째 인자인 callback 함수를 전달하면 됩니다.

 

다음 코드는 this.state.num의 값을 갱신 후 콘솔에 값을 출력하는 코드를 callback 함수로 전달합니다.

twiceButtonClick = () => {
  this.setState(
    { num: this.state.num * 2 }, 
    () => {
      console.log("state : " + this.state.num);
    }
  );
};

실행 결과

[* 2] 버튼을 클릭 전 결과입니다.

[* 2] 버튼 클릭 전

[* 2] 버튼을 클릭 후 결과입니다.

[* 2] 버튼 클릭 후

또 다른 방법으로는 생명주기 함수인 componentDidUpdate를 사용하는 것입니다.

twiceButtonClick = () => {
  this.setState({ num: this.state.num * 2 });
};
  
componentDidUpdate(prevProps, prevState) {
  if (this.state.num !== prevState.num) {
    console.log("state : " + this.state.num);
  }
}

이전 값과 현재 값을 비교해서 값이 일치하지 않으면, 특정 기능 또는 동작이 실행되는 코드를 작성합니다.


Function 컴포넌트의 setState Callback

React 16.8 이후 도입된 Hook은 useState 함수를 사용해서 state를 갱신할 수 있습니다.

 

단, useState 함수는 두 번째 인수가 존재하지 않습니다.

 

대신 useEffect에 선택적 인수로 두 번째 인수인 의존성 배열을 사용합니다.

 

다음은 위에서 작성한 Class 컴포넌트를 Hook으로 변경한 코드입니다.

function App() {
  const [num, setNum] = useState(1);

  function twiceButtonClick() {
    setNum(num * 2);
  }

  function initButtonClick() {
    setNum(1);
  }

  useEffect(() => {
    console.log("num : " + num);
  }, [num]);

  return (
    <div className="App">
      <button onClick={() => { twiceButtonClick(); }} >
        * 2
      </button>
      <button onClick={() => { initButtonClick(); }} >
        Clear
      </button>
      <div>{num}</div>
    </div>
  );
}

 

App 컴포넌트에서 핵심 코드는 useEffect 함수입니다.

useEffect(() => {
  console.log("num : " + num);
}, [num]);

두 번째 인자로 [num]을 전달하여 num이라는 state가 변경되면, useEffect 함수가 실행되도록 합니다.

 

출처: https://developer-talk.tistory.com/229

반응형
반응형

1. 변경한 state value를 화면에 구현하기

reducer / store 개념을 종합하여 버튼을 눌렀을때 값이 변화하는 logic을 구현해본다.

1-1. addEventListener을 활용해서 action dispatch

HTML에서 생성한 버튼 tag(속성)에 click 이벤트를 추가하고,
클릭 시 action dispatch를 발동해서 state + 1 을 하도록 구현한다.

//Vanilla
import { createStore } from "redux"

const add = document.getElementById("add")
const minus = document.getElementById("minus")
const number = document.querySelector("span")

const reducer = (state = 0, action) => {
  if(action.type === "ADD"){
    state = state + 1
  }
  if(action.type === "MINUS"){
    state = state - 1
  }
  console.log(state)
  return state
}
const countStore = createStore(reducer)

const addHandler = () => {
  countStore.dispatch({type:"ADD"})
}

add.addEventListener("click", addHandler)

const minusHandler = () => {
  countStore.dispatch({type:"MINUS"})
}

minus.addEventListener("click", minusHandler)

위 코드에서

add tag(더하기 버튼)에 addEventListener를 통해 클릭하였을때("click"), addHandler 함수를 실행하도록 구성하였다.

이때 addHandler 함수는 action을 dispatch해서, store가 이를 인식하여 reducer를 통한 state value 변화를 일으키도록 유도한다.

위 logic에 대해 log 출력을 살펴보면 state value가 정상적으로 변화함을 확인하였다.

1-2. Subscribe

Reducer가 호출될 때 subscribe되어있는 함수 및 객체를 호출하는 동작 방식

const stateChanged = () => {
  console.log("Reducer Changed!")
}

countStore.subscribe(stateChanged)

위 코드를 실행하게 되면 아래와 같이 Reducer가 호출되면서 subscribe로 정의된 함수도 같이 호출된다.

Store의 subscribe 함수를 이용하게되면 Reducer가 호출되는 시점에서 subscribe를 통해 정의한 함수를 호출 및 동작할 수 있게 된다.

1-3. subscirbe를 활용한 변화한 값 화면에 구현하기

click 이벤트 실행 시 reducer가 호출된다는 원리를 활용해서 subscribe도 같이 동작하도록 구현한다.

const stateChanged = () => {
  number.innerText = countStore.getState()
}

countStore.subscribe(stateChanged)

위 코드와 같이 subscribe를 통해 변화한 상태값을 화면에 구현하도록 HTML tag(속성)과 연결하는 logic을 구현하면, 화면에 해당 값을 그대로 출력할 수 있게 된다.

2. 정리

redux 기본 구조를 간략하게 정리하면 다음과 같다.

  • redux는 React와는 전혀 다르고, 하나의 독립적인 기능을 제공하는 라이브러리이다.
  • redux의 핵심 구조는 store, reducer가 있고 이 둘을 연결하면 reducer 내부의 데이터의 흐름을 일방향화하여 관리를 유용하게 할 수 있다.
  • redux 내부의 데이터는 store에 종속되고, 반드시 redux logic에 의해서만 변화할 수 있다.
  • redux 내부적인 데이터 변화보다는, redux와 action을 연결하여 외부적으로 연결하는 방식을 사용한다.
  • action은 dispatch를 통해 연결할 수 있고, subscribe를 통해 reducer가 호출되는 시점에서 상태값을 얻거나(getState) 화면에 구현하는 함수 등을 구성할 수 있다.
  • dispatch/subscribe/getState 등은 모두 Store가 제공하는 함수들이다.

 

 

출처: https://velog.io/@gyrbs22/Redux-redux-%EA%B8%B0%EB%B3%B8-action-dispatch-subscribe

반응형
반응형

If you are using version > 2.13 then you won't need to install dotenv anymore because it's already built in https://nuxtjs.org/docs/directory-structure/nuxt-config/#runtimeconfig

.env support Similar to vue-cli (*), .env file will be always loaded via dotenv and is accessible via process.env and options._env. process.env is updated so one can use it right inside nuxt.config for runtime config. Values are interpolated and expanded with an improved version of dotenv-expand. .env file is also watched to reload during nuxt dev. Path can be set via cli --dotenv or disabled by --dotenv false.

I created the .env.xxx files and created the corresponding scripts

 

반응형

+ Recent posts