반응형

package-lock.json은 왜 생성될까?


우리가 기존에 사용하는 package.json 만으로는 정보가 부족하기 때문입니다.

package.json 에서는 버전정보를 저장할 때 version range 를 사용합니다.

“내가 사용할 패키지의 버전은 1.2.7버전이다.” 라고 말하는 대신 “나는 1.2.7버전 이상의 패키지를 사용할거다.”처럼 말하는 방식인데요. (전자처럼 버전정보를 명시하는 것도 가능합니다.)

협업을 하기위해 같은 package.json 을 사용해 각자의 컴퓨터에 같은 패키지들을 설치해서 같은 개발환경을 구성하게됩니다.

하지만 몇가지 조건이 들어맞아버리는 불행한(..) 상황이 오게되면, 같은 package.json 을 사용해서 npm install 을 진행하더라도 서로 다른 node_modules를 생성하는 경우가 발생합니다.

  • npm의 버전이 다른 경우, npm의 알고리즘이 조금씩 다르기 때문에 서로 다른 node_modules 트리가 생성될 수 있습니다.
  • 콕찝어서 버전명을 명시하지않고 version range 를 사용하기 때문에, 새로운 버전의 패키지가 배포된 이후 설치를 진행할 경우 최신 버전으로 설치될 수 있습니다.
  • 내가 사용하고 있는 패키지가 의존하고 있는 패키지가 새로운 버전으로 배포되었을 경우, 다른 node_modules 트리가 생성될 수 있습니다.

 

첫번째 상황 같은 경우에는 협업하는 멤버들이 npm --version 으로 버전을 확인한 뒤, npm 버전을 일치시킨 후 작업하면 예방할 수 있는 상황입니다.

하지만 다른 상황들에서는 다음과같은 일들이 발생할 수 있습니다.

개발자1: 테스트가 계속 실패해.

개발자2: 어제 내가 테스트할 땐 잘 됐는데??

개발자1: 왜이러지..

.. 몇 시간 후

개발자1: 아직도 안돼..

개발자2: 뭐가 문제인 것 같아?

개발자1: range-parser문제인 것 같아.

개발자2: range-parser 버전 몇이야?

개발자1: 2.0.1

개발자2: 나랑 버전이 다르네; 오늘 아침에 새로운 버전으로 릴리즈되었나본데?

난감하죠.

이런 불행한 상황을 피해가기위해 태어난 것이 package-lock.json 입니다.

package.json 에는 "~3.3.8" 으로 적혀있지만, package-lock.json 에는 "3.3.16"으로 정확한 버전명이 적혀있습니다.

package-lock.json  node_modules 구조나 package.json 이 수정되고 생성될 때 당시 의존성에 대한 정확하고 구체적인 정보를 품고 자동으로 생성됩니다.

npm install 명령어를 입력하면 태어난다고 생각하면 되겠네요.

또한 package-lock.json 이 존재할 때에는 npm install 의 동작방식이 조금 변하는데요.

package.json 을 사용하여 node_modules 를 생성하지않고 package-lock.json 을 사용하여 node-modules 를 생성합니다.

정리를 해보자면, package-lock.json 은 개발자들이 동일한 node_module 트리를 생성해서 같은 의존성을 설치할 수 있도록 보장해주는 고마운 녀석이라고 할 수 있겠습니다.

결론은.

 package-lock.json 을 레포지토리에 같이 커밋하자. ”

 

포스팅 끝!

.

.

.

..을 내려고했지만 궁금했던 점.

 package.json  package-lock.json 으로 나눠둔 걸까요?


애초에 package.json 에 정확한 버전명을 적어놓으면 해결되는 문제 아닐까? 라는 의문점이 들어서 찾아봤습니다.

앞서 package.json 에서는 version range 를 사용한다고 했습니다.

version range 는 다음과 같이 사용하는데요.

  • 1.4.0: 정확하게 1.4.0버전
  • >1.4.0 : 넘버링이 1.4.0보다 큰 버전
  • <1.4.0: 넘버링이 1.4.0보다 작은 버전
  • >=1.4.0 : 넘버링이 1.4.0보다 크거나 같은 버전
  • <=1.4.0 : 넘버링이 1.4.0보다 작거나 같은 버전
  • 1.4.0 || >= 2.4.0 : 정확하게 1.4.0버전이거나, 넘버링이 2.4.0보다 같거나 큰 모든 버전

 

만약 package.json 에 패키지 버전을 콕 찝어 정해놓는다면,

프로젝트에서 사용하고 있는 패키지의 중요한 버그 수정이 이루어질 때 마다 프로젝트의 package.json 에 적혀있는 버전도 수정해주어야하기 때문입니다.

모든 크고 작은 패키지들의 릴리즈에대해 항상 추적하고 수정해야하는 엄청난 귀찮음과 수고스러움을 version range 가 해결해주고 있었습니다.

역시 괜히 이렇게 나눠둔 것이 아니었네요!

 

포스팅 끝!

.

.

.

을 내려고했지만 하나만 더.

npm ci 명령어


npm ci 는 npm@5.7.1 부터 지원되는 명령어입니다.

package-lock.json 이 존재하면 package.json 대신, package-lock.json 을 이용해서 패키지들을 설치하는 명령어입니다.

“아니 걍 npm install로 설치해도 package-lock.json있으면 lock으로 설치하는거 아닌가”

npm blog{:target=”_blank”} 에 따르면 node_modules 가 없으면, npm install 보다 npm ci  2배 이상 더 빠르다고 합니다.

오오..

그래서 저도 사용해봤는데요.

npm install 명령어로 설치

npm ci 명령어로 실행

작은 프로젝트임에도 설치 시간이 차이가 나네요.

production으로 빌드할때 node_modules가 항상 비워져있는 환경이라면, npm ci 명령어를 사용하는 것도 좋을 듯 합니다!

반응형
반응형

Vue.js의 Vuex Store 패턴에서 Vue.js Component에 Store를 바인딩 시키는 여러가지 방법을 알아보자. Vuex의 Store를 Component에 바인딩하는 방법에는 여러가지가 있으며 상황에 따라 그 쓰임이 다르다. 그 중에서 가장 실무에 적합하고 가독성이 좋은 방법이 무엇인지 알아보고 활용해보자.

해당 포스트에서는 Vuex의 설명과 사용법에 대해서는 언급하지 않았으며, 오로지 Vuex Store를 컴포넌트에 바인딩하는 방법을 설명한다.

프로젝트의 규모가 크거나 작든 Vuex를 사용할 때는 Store를 Module 별로 분리하는 것이 바람직하다. 그렇지 않으면 각 컴포넌트마다 바인딩 된 Store의 state들이 다른 컴포넌트의 루틴에 의해 오염될 가능성과 코드의 복잡성이 높아지게 되어 있다.

이번 포스트에서는 모듈별로 분리된 Store를 각 컴포넌트에서 효율적으로 바인딩시키는 여러 가지 방법들과 코드 스타일을 알아보자.

Basic

Vue 공식 API에서는 명시된 기본 Store 바인딩 방법이다.

 

위 내용은 mapState라는 Helper를 이용하여 객체의 형태로 count를 바인딩 한 형태이고 아래처럼 state의 이름을 그대로 상속받아 정의할 수도 있다.

 

기본적인 바인딩 방법은 가장 단순한 구조를 가진 Store를 바인딩하였을 때이다. 하지만 Store가 여러 모듈별로 분리되어 있고 하나의 컴포넌트에서는 여러 Store 모듈을 바인딩해야 한다면 매우 복잡해질 것이다.

아래 가정을 가지고 각 바인딩 Style을 살펴보자.

각각 User, Book 이라는 Store 모듈이 존재하고, User A/B 경로에 있으며, Book A/B/C 경로에 위치한다.

프로젝트 구조는 다음과 같다.

 

Style 1 - Vuex Helper

 

또는

 

특정 경로에 포함된 Store 모듈을 사용하기 위해서는 해당 경로를 모두 명시해줘야 한다. 이렇게 사용을 한다면 어느 경로에 있든 서로 다른 Store 모듈을 하나의 컴포넌트 또는 여러 컴포넌트에서 바인딩하여 사용이 가능하다. 하지만 위 예제에서 A/B/C의 단순하고 짧은 명칭이지만 폴더나 Store 모듈의 명칭이 꽤나 길다면 역시나 가독성이 좀 떨어질 것이다. 그래서 우리는 Vuex에서 제공하는 Namespace Helper를 생성할 수 있는 createNamespacedHelpers 이용하여 바인딩하는 것을 가장 인상적으로 볼 수 있다.

Style 2 - createNamespacedHelpers 1

 

또는

 

모듈의 이름이 길고 구조가 복잡하다면 createNamespacedHelpers를 사용하여 바인딩한다면 computed 또는 methods가 간결하고 뛰어난 가독성을 보이는 것을 알 수 있다.

Store 모듈들의 경로를 computed와 methods에 정의를 안 했을 뿐이지 무엇이 다르겠냐고 한다면 생각해보자. 우리는 개발을 하면서 코드를 살펴볼 때나 구현을 할 때 가장 상단에 삽입하거나 명시해 놓은 구현체는 자주 보지 않는다. 오히려 data, computed, methods를 가장 많이 볼 것이다. 이러한 상황에서 수많은 모듈의 경로가 명시되어 있다면 state나 mutation, action의 개체들을 찾기 어려울 것이다.

Style 3 - createNamespacedHelpers 2

그러면 여기서 좀 더 깊이 한번 보자.

만약 createNamespacedHelpers를 사용을 한다고 하지만 만약 하나의 컴포넌트에 서로 다른 경로에 있는 Store 모듈을 여러 개 바인딩할 경우에는 어떻게 해야 할까?
지금까지 Book이라는 모듈만을 가지고 예제를 보았지만, 여기에서 User 모듈을 추가해보자.

 

아래처럼 정의도 가능하다.

 

이렇게 한다면 하나의 컴포넌트에서도 많은 모듈을 바인딩하기가 쉬우며, 코드의 가독성 역시 좋아진다.


각 개발자마다 코드 스타일은 모두 다르고 해당 포스트에 언급한 내용 이외에 더 좋은 방법이 있을 수 있다. 다만 중요한 것은 개발하기 전 또는 하면서 어느 방법이 가장 좋은지 몸소 느끼며 차근차근 수정해 나아가는 것이 중요하다고 본다. 어느 스타일이든 결과는 같겠지만 업무/개발의 속도는 확연히 차이가 날 것이다.

 

출처: https://kdydesign.github.io/2019/04/06/vuejs-vuex-helper/

반응형
반응형

컴포넌트로 onClick과 같은 이벤트 핸들러를 어떻게 전달 할까요?

자식 컴포넌트에 프로퍼티로 이벤트 핸들러와 다른 함수들을 전달합니다.

<button onClick={this.handleClick}>

핸들러 안에서 부모 컴포넌트에 접근할 필요가 있으면 컴포넌트 인스턴스에 함수를 바인딩해 주어야 합니다.

컴포넌트 인스턴스로 함수를 어떻게 바인딩할까요?

사용하고 있는 문법과 빌드 단계에 따라 this.props, this.state와 같은 컴포넌트의 어트리뷰트에 함수들이 확실히 접근할 수 있도록 만드는 방법은 여러 가지가 있습니다.

생성자에서 바인딩하기 (ES2015)

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

클래스 프로퍼티 (Stage 3 Proposal)

class Foo extends Component {
  // 주의: 이 문법은 실험단계이며 아직 표준이 아닙니다.
  handleClick = () => {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

render 메서드 안에서 바인딩하기

class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
  }
}

주의

Function.prototype.bind를 render 메서드에서 사용하면 컴포넌트가 렌더링할 때마다 새로운 함수를 생성하기 때문에 성능에 영향을 줄 수 있습니다.

render 메서드 안에서 화살표 함수 사용

class Foo extends Component {
  handleClick() {
    console.log('Click happened');
  }
  render() {
    return <button onClick={() => this.handleClick()}>Click Me</button>;
  }
}

주의

render 메서드 안에서 화살표 함수를 사용하면 컴포넌트가 렌더링할 때마다 새로운 함수를 만들기 때문에 엄격한 비교에 의해 최적화가 깨질 수 있습니다.

render 메서드 안에서 화살표 함수를 사용해도 괜찮을까요?

이 방법은 대체로 사용해도 괜찮고, 콜백 함수로 매개변수를 전달해 주는 가장 쉬운 방법입니다.

성능 문제가 있다면 반드시 최적화를 해야 합니다.

바인딩이 필요한 이유는 무엇일 까요?

자바스크립트에서 아래 두 개의 코드 조각은 동일하지 않습니다.

obj.method();
var method = obj.method;
method();

바인딩 메서드는 두 번째 코드 조각이 첫 번째 코드조각과 같은 방식으로 작동하도록 만들어 줍니다.

일반적으로 React에서 다른 컴포넌트에 메서드를 전달해 줄 때만 바인딩해 주면 됩니다. 예를 들어 <button onClick={this.handleClick}>는 this.handleClick을 전달하여 바인딩합니다. 그렇지만 render 메서드나 생명주기 메서드는 다른 컴포넌트로 전달하지 않기 때문에 바인딩할 필요가 없습니다.

Yehuda Katz의 글에서 바인딩이 무엇인지, JavaScript에서 어떻게 함수가 작동하는지에 대해 상세히 알 수 있습니다.

왜 컴포넌트가 렌더링할 때마다 함수가 호출될까요?

컴포넌트로 함수를 전달할 때 호출하지 않는지 확인합니다.

render() {
  // 잘못된 방법: handleClick은 레퍼런스로 전달되지 않고 호출되었습니다!
  return <button onClick={this.handleClick()}>Click Me</button>
}

위와 같은 방식이 아니라 괄호 없이 함수 그 자체를 전달해야 합니다.

render() {
  // 올바른 방법 : handleClick이 레퍼런스로 전달되었습니다.
  return <button onClick={this.handleClick}>Click Me</button>
}

이벤트 핸들러나 콜백에 어떻게 매개변수를 전달할나요?

이벤트 핸들러에 화살표 함수를 사용하여 감싼 다음에 매개변수를 넘겨줄 수 있습니다.

<button onClick={() => this.handleClick(id)} />

.bind를 호출한 것과 같습니다.

<button onClick={this.handleClick.bind(this, id)} />

예시: 화살표 함수를 이용하여 매개변수 전달하기

const A = 65 // ASCII character code

class Alphabet extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      justClicked: null,
      letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i))
    };
  }
  handleClick(letter) {
    this.setState({ justClicked: letter });
  }
  render() {
    return (
      <div>
        Just clicked: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} onClick={() => this.handleClick(letter)}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

예시: data-attributes를 사용해서 매개변수 전달하기

다른 방법으로 이벤트 핸들러에 필요한 데이터를 저장하기 위해 DOM API를 사용할 수 있습니다. 이 방법은 아주 많은 요소를 최적화하거나 React.PureComponent 동일성 검사에 의존하는 렌더링 트리를 사용할 때 고려해 볼 만합니다.

const A = 65 // ASCII character code

class Alphabet extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = {
      justClicked: null,
      letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i))
    };
  }

  handleClick(e) {
    this.setState({
      justClicked: e.target.dataset.letter
    });
  }

  render() {
    return (
      <div>
        Just clicked: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} data-letter={letter} onClick={this.handleClick}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

어떻게 함수가 너무 빨리, 너무 많이 호출되는 것을 막을 수 있나요?

onClick 또는 onScroll과 같은 이벤트 핸들러를 사용하고 있을 때 콜백이 너무 빠르게 호출되지 않도록 콜백이 실행되는 속도를 제어할 수 있습니다. 다음의 함수들을 사용하면 됩니다.

  • throttling: 시간 기반 빈도에 따른 변경 샘플링 (예시 _.throttle)
  • debouncing: 비활성 주기 이후에 변경 적용 (예시 _.debounce)
  • requestAnimationFrame throttling: requestAnimationFrame (예시 raf-schd)을 기반으로 한 변경 샘플링

throttle과 debounce 함수를 비교하고 싶으면 시각화를 확인하면 됩니다.

주의

_.debounce, _.throttle, raf-schd는 지연되는 콜백을 취소하는 메서드 cancel을 제공합니다. componentWillUnmount에서 이 함수를 사용하거나 또는 지연된 함수 내에서 컴포넌트가 마운트가 되어있음을 확인해야 합니다.

Throttle

Throttling은 함수가 주어진 시간 동안에 한 번 이상 호출되는 것을 막습니다. 아래는 “click” 핸들러에 throttling을 사용하여 초당 한 번만 호출되도록 한 예시입니다.

import throttle from 'lodash.throttle';

class LoadMoreButton extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.handleClickThrottled = throttle(this.handleClick, 1000);
  }

  componentWillUnmount() {
    this.handleClickThrottled.cancel();
  }

  render() {
    return <button onClick={this.handleClickThrottled}>Load More</button>;
  }

  handleClick() {
    this.props.loadMore();
  }
}

Debounce

Debouncing은 함수가 마지막으로 호출된 후 특정 시간까지 실행되지 않도록 해줍니다. 빠르게 발행하는 이벤트(예시 스크롤, 키보드 이벤트)의 응답으로 어떤 비싼 계산을 수행해야 할 때 사용하면 좋습니다. 아래의 예시는 250 밀리초 이내의 텍스트 입력을 Debouncing했습니다.

import debounce from 'lodash.debounce';

class Searchbox extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.emitChangeDebounced = debounce(this.emitChange, 250);
  }

  componentWillUnmount() {
    this.emitChangeDebounced.cancel();
  }

  render() {
    return (
      <input
        type="text"
        onChange={this.handleChange}
        placeholder="Search..."
        defaultValue={this.props.value}
      />
    );
  }

  handleChange(e) {
    this.emitChangeDebounced(e.target.value);
  }

  emitChange(value) {
    this.props.onChange(value);
  }
}

requestAnimationFrame throttling

requestAnimationFrame은 렌더링 성능을 위해 브라우저에서 최적화된 시간에 함수가 실행되도록 함수를 큐잉하는 방법입니다. requestAnimationFrame의 큐로 들어간 함수는 다음 프레임에서 실행됩니다. 브라우저는 1초당 60 프레임(60 fps)을 보장하기 위해 열심히 일합니다. 하지만 브라우저가 이를 하지 못할때 저절로 프레임을 제한합니다. 예를 들면 한 기기가 30 fps만 처리할 수 있다면 1초 동안 30 프레임만 얻을 수 있습니다. throttling을 위해 requestAnimationFrame을 사용하면 1초에 60번 이상 업데이트하는 것을 막을 수 있습니다. 1초당 100번 업데이트하도록 브라우저에 일을 만들어 주어도, 유저는 이를 확인할 수 없습니다.

주의

이 기법을 사용하면, 프레임에 가장 마지막으로 게재된 값만 사용하게 됩니다. 최적화가 어떻게 작동하는지에 대한 예시는 MDN에서 확인할 수 있습니다.

import rafSchedule from 'raf-schd';

class ScrollListener extends React.Component {
  constructor(props) {
    super(props);

    this.handleScroll = this.handleScroll.bind(this);

    // 업데이트 일정을 정하는 함수를 만듭니다.
    this.scheduleUpdate = rafSchedule(
      point => this.props.onScroll(point)
    );
  }

  handleScroll(e) {
    // 스크롤 이벤트를 받게 되면 업데이트를 일정에 추가합니다.
    // 한 프레임 안에 많은 업데이트를 받으면 오직 마지막 값만 게재합니다.
    this.scheduleUpdate({ x: e.clientX, y: e.clientY });
  }

  componentWillUnmount() {
    // 마운트 해제 중에 임시상태의 업데이트들을 모두 취소합니다.
    this.scheduleUpdate.cancel();
  }

  render() {
    return (
      <div
        style={{ overflow: 'scroll' }}
        onScroll={this.handleScroll}
      >
        <img src="/my-huge-image.jpg" />
      </div>
    );
  }
}

속도 제한 테스트 방법

속도 제한 코드가 잘 작동하는지 테스트할 때, 빨리 감기 기능을 사용하는 것이 좋습니다. jest를 사용한다면 mock timers를 빨리 감기 도구로 사용할 수 있습니다. requestAnimationFrame throttling을 사용한다면 애니메이션 프레임의 틱을 제어하기 위한 툴로 raf-stub를 보면 좋습니다.

반응형
반응형

redux 사용전 컴포넌트의 상태 변화의 전달 과정

redux를 사용 후 컴포넌트 외부에서 상태관리 과정

Redux를 사용하는 이유

 

리액트 프로젝트의 경우 대부분의 작업시 부모 컴포넌트를 통해 하위 컴포넌트의 데이터를 업데이트 한다.

(데이터의 연관이 있는 컴포넌트끼리 ref를 사용하여 전달할 수 있으나 코드가 꼬이는 문제로 인해 지양한다)

 

컴포넌트의 갯수가 적을때는 문제가 되지 않지만, 점점 늘어날수록 유지보수의 어려움이 발생한다.

예를 들어 변수명의 변경을 하면 연관된 컴포넌트 파일 모두에 수정을 거쳐야된다.

그러나 리덕스를 사용하게 되면 데이터 상태를 컴포넌트 외부에서 관리하기 때문에 과정을 단순화 시킬 수 있게 된다.

 

- Reducer : 업데이트 로직을 정의하는 함수

 

< Redux 흐름 >

subscribe(상태 변화 감지요청) >> action(상태 변화) >> dispatch(상태업데이트, store에 action 전달)

>> Store(state 갱신) >> listener(상태 변화 알림) >> 컴포넌트 리렌더링

 

 

- 여러 컴포넌트를 거칠 필요없이 부모 컴포넌트에서 다이렉트로 받는거처럼 리덕스 스토어에서 원하는 상태값을 전달한다.

(단방향 데이터 흐름으로 데이터 구조의 단순화)

 

< Redux 의 3 원칙 >

1. Single source of truth
    애플리케이션 내에 Store는 반드시 1개 뿐. Store는 반드시 1개만 존재한다.

2. State is read-only
    state를 직접 변경해서는 안.된.다.
    state를 변화시키는 유일한 방법은 Action을 Reducer에 dispatch(송신, 전달)하는 방법 뿐이다.
    즉, state의 변경은Reducer만 할 수 있다. Reducer 이외의 공간에서는 state는 읽기 전용인 것이다.

 

3. Changes are made with pure functions
    Reducer는 순수 함수여야만 한다.
    Reducer 함수는 parameter로 기존의 state와 Action을 받는데, 

    Reducer 함수는 기존의 state를 직접 변경하지 않고, 새.로.운 state object(객체)를 작성해서 return해야한다.


useReducer

useReducer와 context api 로 redux의 기능을 대부분 구현할 수 있다

(그러나 프로젝트의 규모가 크다면 useReducer와 context api 조합의 한계로 비동기적인 작업시 불편하다)

 

import React, { useState, useReducer } from 'react';

const initialState = { //state 정의
	winner: '',
    turn : 'O',
    tableData : [['','',''], ['','',''], ['','','']],
};

const reducer = (state, action) => { //state 변화 정의
	
};
const [state, dispatch] = useReducer(reducer, initialState);

 

- 여러개의 state를 하나의 state로 만들어 state의 갯수를 줄여준다

 

< Redux와 useReducer 차이 >

 

1. 리덕스는 dispatch를 통해 state가 동기적으로 변경, useReducer는 비동기적으로 변경

 

table 최적화 하기

 

useEffect를 사용하여 렌더링 될 때 console.log로 두번째인자(바뀌는 값)을 넣어서 어떠한 값의 변화로 인한 리렌더링인지 확인할 수 있다.

최종 하위 컴포넌트(td) 값의 변화로 리렌더링 될때 부모 컴포넌트까지 렌더링되지 않도록 memo를 사용하여 props의 값을 기억해준다.

 

 

** React.memo : 기억해둔 props의 값이 같다면 기억해둔 값을 재사용, 다르다면 리렌더링

 

** useCallback :  함수 자체를 메모이징해서 재사용한다. 불필요한 렌더링을 줄일수 있다.


//부모 컴포넌트
import React, { useReducer, createContext } from 'react';

const Context = createContext({initial});
const value = useMemo(() => ({tableData : state.tableData, dispatch}), [state.tableData]);

<Context.Provider value={ value }>
	<Form />
    <Table />
</Context.Provider>


//하위 컴포넌트
import React, { useContext } from 'react';
import { Context } form '부모컴포넌트';

const Table = () => {
	const { tableData } = useContext(Context);
    ...
};

export default Table;

Context Api

context api를 제외하고 useReducer만 사용할 경우, dispatch 와 reducer를 통해 변경할 데이터(action)을 props로 최종 변경할 컴포넌트까지 전달해주어야한다.

- 자식 컴포넌트 뿐 아니라 해당 데이터를 사용할 컴포넌트 모두에게 다이렉트로 전달할 수 있다.

 

1. createContext(기본값) 으로 Context 객체를 생성

2. Context.Provider : 하위 컴포넌트에 접근 가능하도록 명시

    - Context.Provider 의 value 속성으로 데이터 전달

    - value 속성에 객체로 명시해주면 렌더링시 계속 새로운 객체를 만들기 때문에 최적화에 문제가 발생, 

        >> useMemo를 사용(캐싱)하여 메모이징하여 해결한다. dispatch는 항상 같으므로 변경될 값에 기재하지 않아도된다.

 

3. useContext : Context(부모 컴포넌트에서 전달될 props)를 자식컴포넌트에서 불러오도록 해준다.

 

< Context API 최적화 >

 

memo, useMemo를 적절히 사용하여 캐싱을 통해 최적화

 

 

 

출처: https://recoderr.tistory.com/50

반응형

+ Recent posts