본문 바로가기

javascript/react

[React] react-redux

 

[React] redux에 대한 개념

Redux란? 리액트의 컴포넌트들은 상태 기반으로 동작한다. 특정 컴포넌트 내부의 상태가 어떤 이벤트에 의해 변경되면, 해당 상태가 갱신되면서 컴포넌트 함수가 다시 실행된다. 평가 결과 기존 v

blaxsior-repository.tistory.com

이전 글에서 이어집니다.

 

 앞서 언급했듯이 리액트를 redux와 함께 사용하면 컴포넌트로부터 상태를 격리하여 전역적으로 관리할 수 있다는 장점이 존재한다. 단 redux 그대로 리액트에 적용할 수는 없는데, 리액트의 모든 것은 리액트 내부적으로 관리하는 State 기반으로 관리되기 때문이다.

 따라서 리액트에서 특정 값을 사용하기 위해서는 redux의 Store을 리액트와 동기화할 방법이 필요하다. react-redux 는 이러한 필요를 만족하는 라이브러리로, 함수 컴포넌트에 사용되는 hook 들은 리액트의 hook 및 API에 기반하여 redux가 관리하는 State을 제공한다.

react-redux 라이브러리가 함수 컴포넌트에 대해 제공하는 hook들은 다음과 같다.

  • useReduxContext : 리액트의 Context에 기반한 store을 제공한다. 실제로 사용할 일은 없어 보인다.
  • useStore : useReduxContext로부터 추출된 store을 반환한다. dispatch를 통해 값을 변경할 수 있다.
  • useSelector : store 가 유지하는 특정 데이터 값을 추출해 제공한다.
  • useDispatch : redux의 store에서 사용했던 dispatch 함수를 제공한다. 

해당 라이브러리에 대해 구체적으로 살펴보자.

 

useReduxContext

리액트 프로젝트를 관리하면서 실제로 사용할 일은 거의 없을 hook이다. Redux에 대한 Context을 제공한다.

useReduxContext : 

https://github.com/reduxjs/react-redux/blob/93d5ee5e1fd94b76040645336d483e9f9574de20/src/hooks/useReduxContext.ts

내부적으로 리액트의 Context API를 사용하여 ReactReduxContext을 등록하고, 해당 context를 반환하는 구조이다.

 

ReactReduxContext :

https://github.com/reduxjs/react-redux/blob/93d5ee5e1fd94b76040645336d483e9f9574de20/src/components/Context.ts

이때 ReactReduxContext는 Context API를 통해 생성된 컨텍스트로, store 등의 정보를 지니게 된다. 초기에는 null로 초기화 되어 있는데, Context를 Provider 없이 사용할 생각이 없기 때문에 null로 선언하더라도 문제가 없는 것으로 보인다.

 

Provider : 

https://github.com/reduxjs/react-redux/blob/93d5ee5e1fd94b76040645336d483e9f9574de20/src/components/Provider.tsx

 리액트에서 Context를 사용하기 위해서는 사용할 컴포넌트 범위에 Provider을 제공해야 한다. 이때 react-redux의 Provider 함수는 위 ReactReduxContext에 대한 Provider을 구현한다. 해당 함수 내에서 useEffect나 useMemo를 이용하여 상태의 변화를 감지하여 리액트의 상태 관리 체계에 포함된다.

 Context에 의해 설계되었기 때문에, react-redux 라이브러리를 사용하려면 Provider을 제공해야 한다.

 

useStore

redux에서 사용하는 store을 ReactReduxContext 컨텍스트로부터 추출하여 반환한다.

https://github.com/reduxjs/react-redux/blob/93d5ee5e1fd94b76040645336d483e9f9574de20/src/hooks/useStore.ts

 내부적으로 createStoreHook이라는 함수를 이용한다. 이때 해당 함수에 어떤 인자도 넘기지 않으면 자동으로 context에  ReactReduxContext가 할당되는데, 해당 Context을 useContext에 전달하여 store을 추출하여 반환한다.

 store가 context에 등록되어 있기 때문에, 해당 객체를 이용하여 redux 메서드를 이용해도 업데이트가 수행된다. 그렇기는 해도 store의 값에 접근하기 위한 개별적인 hook이 존재하므로, 공식문서에서는 전용 hook의 사용을 권고한다.

useSelector

store으로 관리하는 특정 데이터를 ref 기반으로 추출한다.

https://github.com/reduxjs/react-redux/blob/93d5ee5e1fd94b76040645336d483e9f9574de20/src/hooks/useSelector.ts

createSelectorHook은 파라미터로 context를 가지며, 디폴트 파라미터는 위에서 언급한 ReactReduxContext이다. 해당 함수는 useSelector 함수를 반환하는데, 이 함수가 우리가 실제로 사용할 hook에 해당한다. 

useSelector은 기존 Context로부터 store을 출력한다. 이때 store로부터 일부 데이터들을 출력하여 useSyncExternalStoreWithSelector 함수에 전달하고, 선택한 특정 state을 전달받는다. 해당 함수는 다음과 같다.

https://github.com/facebook/react/blob/6bce0355c3e4bf23c16e82317094230908ee7560/packages/use-sync-external-store/src/useSyncExternalStoreWithSelector.js

해당 함수에서는 내부적으로 useRef hook을 이용하여 selector을 통해 선택된 변수의 값을 추출하고, 해당 값을 useMemo 및 memoization 기법 등을 이용 +  inst을 이용하여 여러가지 내부 로직을 수행한다.

이후 value가 변경될 때마다 inst의 값을 갱신하도록 useEffect를 구성한다.

useDispatch

store의 Dispatch 함수를 추출하여 반환한다. 

https://github.com/reduxjs/react-redux/blob/93d5ee5e1fd94b76040645336d483e9f9574de20/src/hooks/useDispatch.ts

 

사용 방법

  1. redux의 createStore을 이용하여 store 객체를 생성한다. 이 과정까지는 redux와 동일하다.
  2. react-redux의 Provider 컴포넌트로 사용 범위를 정하고, store을 전달한다.
  3. useSelector hook을 이용하여 store 내의 값을 선택한다.
  4. useDispatch hook을 이용하여 dispatch 함수를 이용한다.

 

1. redux 설정 

const defaultState = { counter: 0, toggle: true }; // 초기 상태

const counterReducer = (state = defaultState, action) => {
    //some codes by action
}

const store = createStore(counterReducer);

export default store;

 

2. Provider 제공

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
    , document.getElementById('root'));

 

3~4. useSelector 및 useDispatch 사용

const Counter = () => {
  const counter = useSelector(state => state.counter);
  const toggle = useSelector(state => state.toggle);
  //selector을 이용하여 특정 값 store로부터 추출
  const dispatch = useDispatch(); // dispatch 함수 추출

  const incrementHandler = () => {
   dispatch({ type: 'increment' });
  };
  const decrementHandler = () => {
   dispatch({ type: 'decrement' });
  };
  const counterAddHandler = () => {
   dispatch({ type: 'increase', value: 5 });
  };
  const counterToggleHandler = () => {
   dispatch({ type: 'toggle' });
  };
  // 추출한 dispatch 함수의 사용
  
  return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
      {toggle &&
        <React.Fragment>
          <div className={classes.value}>{counter}</div>
          <button onClick={incrementHandler}>Toggle Counter</button>
          <button onClick={counterAddHandler}>Increase by 5</button>
          <button onClick={decrementHandler}>Toggle Counter</button>
        </React.Fragment>
      }
      <div>
        <button onClick={counterToggleHandler}>Toggle</button>
      </div>
    </main>
  );
};

 

결론

react-redux 라이브러리를 이용하면 redux 라이브러리를 리액트 전반에 대해 사용할 수 있다. 이때 함수 컴포넌트에 대한 hook들은 리액트의 Context 기반으로 동작하므로, 해당 Context에 대한 provider을 구현하는 react-redux의 Provider 컴포넌트를 이용하여 redux store 에 접근할 수 있다.

'javascript > react' 카테고리의 다른 글

[React] react-router  (0) 2022.01.06
[React] custom hook  (0) 2021.12.29
[React] redux에 대한 개념  (0) 2021.12.23
[React] memo & useCallback  (0) 2021.12.21
[React] useImperativeHandle을 타입스크립트와 사용하는 방법  (0) 2021.12.19