20210704 TIL

Breadman·2021년 7월 4일
0

항해99

목록 보기
21/28
post-thumbnail

Debounce & Throttle

반복적으로 발생하는 이벤트를 효과적으로 관리하는 방법.

onScroll이나 input의 onChange 같은 이벤트는 매우 좁은 간격으로 발생한다. Chrome의 확장 프로그램 React development tools를 설치하고, onScroll이나 input의 onChange에 setState를 실행하도록 해주면 component의 테두리가 시뻘개진다. 이를 효과적으로 관리해주는 것이 Debounce & Throttle이다.

Debounce

Debounce는 일정 시간 안에 동일한 이벤트가 발생하지 않으면, 가장 최근의 이벤트를 처리한다.

예를 들어 시간을 1초라고 했을 때, 처음 이벤트가 발생하고나면 1초 타이머를 동작시킨다. 타이머가 끝나기 전에 동일한 이벤트가 발생하면 타이머를 초기화시키고 다시 1초를 잰다. 그러다가 1초 안에 동일한 이벤트가 발생하지 않으면, 가장 최근의 이벤트에 대한 처리를 진행한다.

Throttle

Throttle 역시 Debounce랑 유사하게 동작한다. 다만 타이머가 끝날 때마다 이벤트를 처리한다는 것이 다르다.

예를 들어 1초라고 했을 때, Debounce와 동일하게 첫 이벤트 발생 시 1초 타이머를 동작시킨다. 타이머가 끝나기 전에 동일한 이벤트가 발생해도, 타이머를 초기화 시키지 않는다. 대신 첫 이벤트 이후 1초가 지나면, 해당 시점 기준으로 가장 최근에 발생한 이벤트를 처리하고 그 때서야 타이머를 초기화하고 다시 1초를 잰다.

lodash

Debounce와 Throttle을 사용하는 방법으로 lodash 라이브러리가 있다.
lodash에는 Debounce, Throttle 뿐만 아니라 편리한 여러 함수들이 담겨있다.

import _ from 'lodash'

const debounce = _.debounce((e) => {
  console.log(e.target.value);
})

const throttle = _.throttle((e) => {
  console.log(e.target.value);
})

const handleChangeInput = (e) => {
  debounce(e);
  throttle(e);
}

useCallback

functional Component에서 함수를 정의하면, state가 변경될 때마다 새로 선언되고 초기화된다. 따라서 debounce와 throttle을 정의하면, 타이머가 엉망으로 돌아가 원하는대로 동작하지 않는다.

useCallback() 이라는 hook은 useEffect 처럼 특정 값 혹은 state가 변할 때만 새로 초기화가 되도록 할 수 있는 memoization 기능이 있다. 따라서 useCallback() 안에 정의한 debounce와 throttle을 넣어줌으로서, 의도대로 동작시킬 수 있다.

import { useCallback } from 'react'

...
const keypress = useCallback(debounce, []);

const handleChangeInput = (e) => {
  keypress(e);
}

connected-react-router

component가 아닌 외부에서 router 관련 객체들을 사용할 수 있도록 하는 미들웨어.

코딩하다보면 component가 아닌 곳에서 router 객체(history, match, location)가 필요한 경우가 있다. 이 때 사용해줄 수 있는 것이 connected-react-router 라이브러리다.

// configStore.js

import { connectRouter } from 'connected-react-router';
import { createBrowserHistory } from 'history';

// component에서도 관리되는 history 객체를 쓰기 위해선 외부에 export 해줘야한다.
export const history = createBrowserHistory();

const rootReducer = combineReducers({
  user: User,
  router: connectRouter(history),
  // key값은 무조건 router로 고정이다.
});

// thunk에 history 추가.
const middlewares = [thunk.withExtraArgument({ history })];
// App.js
import { ConnectedRouter } from 'connected-react-router';
import { history } from 'configStore.js';

...
// 기존의 Router(ex. BrowserRouter or HashRouter 등)를 변경.
<ConnectedRouter history={history}>
  ...
</ConnectedRouter>

redux-logger

dispatch가 발생하면 이전 store값과 현재 store값을 console에 보여주는 미들웨어.

const middlewares = [thunk];

// 현재 어느 환경인 지 확인. (개발환경, 프로덕션(배포)환경 ...)
const env = process.env.NODE_ENV;

// 개발환경에서만 logger 사용.
if (env === "development") {
  const { logger } = require("redux-logger");
  middlewares.push(logger);
}

Redux Devtools

Redux 상태를 브라우저에서 확인할 수 있는 chrome extension.

세팅은 다음과 같다.

import { createStore, applyMiddleware, compose } from 'redux';

const composeEnhancers =
  typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
        // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
      })
    : compose;

const enhancer = composeEnhancers(
  applyMiddleware(...middlewares)
);

let store = (initialStore) => createStore(rootReducer, enhancer);

패키지를 통해 설정하는 방법도 있다.

$ yarn add redux-devtools-extension
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(...middlewares));

기타

  • 변수값을 객체의 key로 사용하고 싶으면, 대괄호[]로 묶어준다.
const NAME = "john";

const users = {
  [NAME]: {
    age: 28
  }
}

// users = { john: { age: 28 } }
  • history.replace() 는 현재 페이지를 갈아끼우기 때문에, 뒤로가기 했을 때 현재 페이지가 나타나지 않는다.
    (에러 페이지에서 홈으로 가는 버튼에 넣기 좋은 메소드다.)

  • firestore에서 number형 필드값에 특정 값만큼 추가하고 싶으면,
    firebase.firestore.FieldValue.increment(value)를 사용하면 된다.
    여기서 value는 추가하고 싶은 값이다.

const addCommentFB = (comment) => (dispatch, getState) => {
  ...
  const increment = firebase.firestore.FieldValue.increment(1);
  postDB
    .doc(postId)
    .update({commentCnt: increment})
  ...
}
profile
빵돌입니다. 빵 좋아합니다.

0개의 댓글