먼 이웃 컴포넌트에게 prop을 넘길때 prop-drilling이 발생하게 되는데, 이를 해결하기위해 global한 전역 상태 관리 도구인 Redux
를 사용할 수 있다.
Flux
Redux에는 Flux의 개념이 포함되어 있다.
Flux는 facebook에서 만든 오픈소스 라이브러리이지만, 현재는 사용하지 않고있다.
action, dispatcher, store, view 등으로 구성되어 있다.
action -> dispatcher -> store -> view -> action 순으로 반복된다.
dispatcher
모든 데이터의 흐름을 관리하는 중앙 허브의 역할을 한다.
store로의 접근이 가능하다
store
상태 저장소이며, 상태를 업데이트 할 수 있는 함수를 제공한다. 변경 후 view로 전달하고 이후에 view가 갱신된다.
Redux
Redux는 Flux + Reducer 의 개념이 합쳐진 도구이다.
reducer
action과 마지막 store의 상태를 기준으로 새로운 상태를 만들어 준다.
store의 내장 함수인 dispatch를 사용하여 reducer로 전달한다.
규칙
- single source of truth
- 애플리케이션의 모든 상태는 하나의 저장소 안에 저장해야한다.
- 여러개일 경우 충돌 혹은 동기화에 대한 이슈가 발생한다.
- 디버깅과 생산성 향상의 이점을 가지고 있다.
- state is read-only
- 상태는 읽기만 허용된다.
- 액션을 통해서만 변경이 가능하다.
- 변화의 의도를 파악하고 중앙에서 흐름관리를 엄격하게 하기 위함이다.
- Changes are made with pure functions
- 변화는 순수함수로만 해야한다.
- 순수함수 : 외부값에 의존하지않고 매개변수만을 통해 반환값을 만들어 내는 함수
middleware
store.dispatch
함수의 실행 뒤애 어떠한 작업을 하기위해 호출한다.
thunk, saga, logger가 middleware로 많이 사용된다.
redux logger
- prev state와 next state, action등을 나열해 보여준다
- 디버깅을 위해 사용된다
redux-thunk
- thunk : 특정작업을 나중에 하기위해 만들어준 함수이다
- 비동기 작업을 처리할 때 가장많이 사용한다
- 객체대신 함수를 dispatch할 수 있게 해준다
- 낮은 boilerplate와, 이해하기 쉬운 코드가 장점이다
- 서비스가 커질경우 잘못다뤄지면 수없이 많은 콜백 지옥에 빠질 수 있다
redux-saga
- action의 발생여부를 모니터링하다가, 그 뒤 작업을 진행 하도록 한다
- thunk에서 못하는 비동기작업중 기존요청취소, 특정액션 발생시 다른액션 dispatch되는 등의 작업을 할 수 있다
- es6의 제너레이터 문법을 사용해야하므로 러닝커브가 높다
- 초기에 구현해야할 boilerplate가 많다
- 순수함수로 작성되므로 테스트 적용이 쉽다
redux-thunk
와 redux-sage
둘다 비동기 처리를 위한 것으로, 상황에 맞게 사용하면된다.
boilerplate
불필요하고 복잡한, 반복되는 코드 작성을 줄이기 위한 일을 boilerplate라고 한다.
redux에서 자주 사용하는 hook
- useSelector : store에 있는 값을 가져오기위해 사용한다
- createSelector : reselect package에 있는 함수이고, memorization 등 캐싱을 하기위해 사용한다
사용
npm install —save redux react-redux
src내부에 actions, reducer, store 폴더를 각각 생성하여 사용하는 게 일반적이다.
- reducer폴더에서 initialState와 순수함수 작성
- actions폴더에서 액션을 정의한 후, 액션함수 작성
- reducer의 순수함수에서 switch로 정의한 액션마다 state가 어떻게 변할지 정의
store폴더에서 rootReducer를 combineReducers로 설정
- combineReducers는 여러 리듀서들을 합칠 수 있다
1 2 3 4 5 6 7 8 9 10 11
import {combineReducers, createStore} from 'reudx'; import {countReducer} from '../reducers/count'; const rootReducer = combineReducers({ count : countReducer, //리듀서 추가될때마다 위처럼작성 }) const store = createStore(rootReducer); export default store;
최상위 파일(ex. App.js)에서 provider 작성
1 2 3 4 5 6 7 8 9 10 11 12 13
import store from './src/store/store'; import {Provider} from 'react-redux'; import {CounterScreen} from './src/screens/CounterScreen'; export default function App(){ return( <SafeAreaProvider> <Provider store={store}> <CounterScreen/> </Provider> </SafeAreaProvider> ); }
디버깅을 위해 redux-logger 사용
npm install —save redux-logger
store 폴더에서 logger를
applyMiddleware([logger]))
처럼 작성할 수 있다.1 2 3 4 5 6 7 8 9 10 11 12 13 14
import {combineReducers, createStore} from 'reudx'; import {countReducer} from '../reducers/count'; import logger from 'redux-logger'; const rootReducer = combineReducers({ count : countReducer, dayCount : dayCountReducer, //리듀서 추가될때마다 위처럼작성 }) const store = createStore(rootReducer, applyMiddleware([logger])); export default store;
redux가 필요한 컴포넌트에서 dispatch하기
useDispatch()
와useSelector()
hook을 이용해서 redux의 값을 사용한다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import {useDispatch, useSelector} from 'react-redux'; import {deleteCount } from '../actions/counter'; export default () => { const dispatch = useDispatch(); const value = useSelector((state)=>state.count.count); const onPressMinus = useCallback(() => { dispatch(deleteCount); },[]); return( <View>{value}</View> ) }