생태계
Redux는 작은 라이브러리이지만, 그 도구와 확장의 생태계가 널리 퍼져나갈 수 있도록 세심하게 규약과 API를 선택했습니다. 그리고 커뮤니티에서는 다양한 종류의 애드온, 라이브러리, 도구 등을 만들어 왔습니다. Redux를 사용하기 위해 이것들을 꼭 써야 하는 것은 아니지만, 여러분의 앱에서 기능을 구현하고 문제를 해결하는데 많은 도움을 줄 것입니다.
Redux와 관련된 라이브러리, 애드온, 도구에 대한 광범위한 목록을 보고 싶다면 Redux Ecosystem Links을 참고하세요. 또한 React/Redux Links에는 React나 Redux를 배우고 싶은 사람을 위한 튜토리얼과 유용한 자료들이 있습니다.
이 페이지에서는 Redux 메인테이너가 개인적으로 선택했거나, 커뮤니티에 널리 채용된 몇 가지만 소개합니다. 이 목록에 없는 것들도 걱정 말고 써보세요! Redux 생태계는 아주 빠르게 성장하고 있으며, 우리가 전부 써보기에는 시간이 부족했으니까요. 아래 목록은 '개발팀이 추천한' 목록 정도로만 받아들여주세요. Redux와 함께 사용할 수 있는 멋진 도구를 만들었다면 망설이지 말고 풀 리퀘스트를 보내주세요.
Table of Contents
- Ecosystem
Library Integration and Bindings
reduxjs/react-redux
The official React bindings for Redux, maintained by the Redux team
angular-redux/ng-redux
Angular 1 bindings for Redux
ember-redux/ember-redux
Ember bindings for Redux
glimmer-redux/glimmer-redux
Redux bindings for Ember's Glimmer component engine
tur-nr/polymer-redux
Redux bindings for Polymer
lastmjs/redux-store-element Redux bindings for custom elements
Reducers
Reducer Combination
ryo33/combineSectionReducers
An expanded version of combineReducers
, which allows passing state
as a third argument to all slice reducers.
KodersLab/topologically-combine-reducers
A combineReducers
variation that allows defining cross-slice dependencies for ordering and data passing
var masterReducer = topologicallyCombineReducers(
{ auth, users, todos },
// define the dependency tree
{ auth: ['users'], todos: ['auth'] }
)
Reducer Composition
acdlite/reduce-reducers
Provides sequential composition of reducers at the same level
const combinedReducer = combineReducers({ users, posts, comments })
const rootReducer = reduceReducers(combinedReducer, otherTopLevelFeatureReducer)
mhelmer/redux-xforms
A collection of composable reducer transformers
const createByFilter = (predicate, mapActionToKey) =>
compose(
withInitialState({}), // inject initial state as {}
withFilter(predicate), // let through if action has filterName
updateSlice(mapActionToKey), // update a single key in the state
isolateSlice(mapActionToKey) // run the reducer on a single state slice
)
adrienjt/redux-data-structures
Reducer factory functions for common data structures: counters, maps, lists (queues, stacks), sets
const myCounter = counter({
incrementActionTypes: ['INCREMENT'],
decrementActionTypes: ['DECREMENT']
})
Higher-Order Reducers
omnidan/redux-undo
Effortless undo/redo and action history for your reducers
omnidan/redux-ignore
Ignore redux actions by array or filter function
omnidan/redux-recycle
Reset the redux state on certain actions
ForbesLindesay/redux-optimist
A reducer enhancer to enable type-agnostic optimistic updates
Utilities
reduxjs/reselect
Creates composable memoized selector functions for efficiently deriving data from the store state
const taxSelector = createSelector(
[subtotalSelector, taxPercentSelector],
(subtotal, taxPercent) => subtotal * (taxPercent / 100)
)
paularmstrong/normalizr
Normalizes nested JSON according to a schema
const user = new schema.Entity('users')
const comment = new schema.Entity('comments', { commenter: user })
const article = new schema.Entity('articles', {
author: user,
comments: [comment]
})
const normalizedData = normalize(originalData, article)
planttheidea/selectorator
Abstractions over Reselect for common selector use cases
const getBarBaz = createSelector(
['foo.bar', 'baz'],
(bar, baz) => `${bar} ${baz}`
)
getBarBaz({ foo: { bar: 'a' }, baz: 'b' }) // "a b"
Store
Change Subscriptions
jprichardson/redux-watch
Watch for state changes based on key paths or selectors
let w = watch(() => mySelector(store.getState()))
store.subscribe(
w((newVal, oldVal) => {
console.log(newval, oldVal)
})
)
ashaffer/redux-subscribe
Centralized subscriptions to state changes based on paths
store.dispatch( subscribe("users.byId.abcd", "subscription1", () => {} );
Batching
tappleby/redux-batched-subscribe
Store enhancer that can debounce subscription notifications
const debounceNotify = _.debounce(notify => notify())
const store = configureStore({
reducer,
enhancers: [batchedSubscribe(debounceNotify)]
})
manaflair/redux-batch
Store enhancer that allows dispatching arrays of actions
const store = configureStore({
reducer,
enhancers: existingEnhancersArray => [
reduxBatch,
...existingEnhancersArray,
reduxBatch
]
})
store.dispatch([{ type: 'INCREMENT' }, { type: 'INCREMENT' }])
laysent/redux-batch-actions-enhancer
Store enhancer that accepts batched actions
const store = configureStore({ reducer, enhancers: [batch().enhancer] })
store.dispatch(createAction({ type: 'INCREMENT' }, { type: 'INCREMENT' }))
tshelburne/redux-batched-actions
Higher-order reducer that handles batched actions
const store = configureStore({ reducer: enableBatching(rootReducer) })
store.dispatch(batchActions([{ type: 'INCREMENT' }, { type: 'INCREMENT' }]))
Persistence
rt2zz/redux-persist
Persist and rehydrate a Redux store, with many extensible options
const persistConfig = { key: 'root', version: 1, storage }
const persistedReducer = persistReducer(persistConfig, rootReducer)
export const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER]
}
})
})
export const persistor = persistStore(store)
react-stack/redux-storage
Persistence layer for Redux with flexible backends
const reducer = storage.reducer(combineReducers(reducers))
const engine = createEngineLocalStorage('my-save-key')
const storageMiddleware = storage.createMiddleware(engine)
const store = configureStore({
reducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware.concat(storageMiddleware)
})
redux-offline/redux-offline
Persistent store for Offline-First apps, with support for optimistic UIs
const store = configureStore({ reducer, enhancer: [offline(offlineConfig)] })
store.dispatch({
type: 'FOLLOW_USER_REQUEST',
meta: { offline: { effect: {}, commit: {}, rollback: {} } }
})
Immutable Data
ImmerJS/immer
Immutable updates with normal mutative code, using Proxies
const nextState = produce(baseState, draftState => {
draftState.push({ todo: 'Tweet about it' })
draftState[1].done = true
})
Side Effects
Widely Used
reduxjs/redux-thunk
Dispatch functions, which are called and given dispatch
and getState
as parameters. This acts as a loophole for AJAX calls and other async behavior.
Best for: getting started, simple async and complex synchronous logic.
function fetchData(someValue) {
return (dispatch, getState) => {
dispatch({type : "REQUEST_STARTED"});
myAjaxLib.post("/someEndpoint", {data : someValue})
.then(response => dispatch({type : "REQUEST_SUCCEEDED", payload : response})
.catch(error => dispatch({type : "REQUEST_FAILED", error : error});
};
}
function addTodosIfAllowed(todoText) {
return (dispatch, getState) => {
const state = getState();
if(state.todos.length < MAX_TODOS) {
dispatch({type : "ADD_TODO", text : todoText});
}
}
}
listenerMiddleware (Redux Toolkit)
listenerMiddleware is intended to be a lightweight alternative to more widely used Redux async middleware like sagas and observables. While similar to thunks in level of complexity and concept, it can be used to replicate some common saga usage patterns.
listenerMiddleware.startListening({
matcher: isAnyOf(action1, action2, action3),
effect: (action, listenerApi) => {
const user = selectUserDetails(listenerApi.getState())
const { specialData } = action.meta
analyticsApi.trackUsage(action.type, user, specialData)
}
})
redux-saga/redux-saga
Handle async logic using synchronous-looking generator functions. Sagas return descriptions of effects, which are executed by the saga middleware, and act like "background threads" for JS applications.
Best for: complex async logic, decoupled workflows
function* fetchData(action) {
const { someValue } = action
try {
const response = yield call(myAjaxLib.post, '/someEndpoint', {
data: someValue
})
yield put({ type: 'REQUEST_SUCCEEDED', payload: response })
} catch (error) {
yield put({ type: 'REQUEST_FAILED', error: error })
}
}
function* addTodosIfAllowed(action) {
const { todoText } = action
const todos = yield select(state => state.todos)
if (todos.length < MAX_TODOS) {
yield put({ type: 'ADD_TODO', text: todoText })
}
}
redux-observable/redux-observable
Handle async logic using RxJS observable chains called "epics". Compose and cancel async actions to create side effects and more.
Best for: complex async logic, decoupled workflows
const loginRequestEpic = action$ =>
action$
.ofType(LOGIN_REQUEST)
.mergeMap(({ payload: { username, password } }) =>
Observable.from(postLogin(username, password))
.map(loginSuccess)
.catch(loginFailure)
)
const loginSuccessfulEpic = action$ =>
action$
.ofType(LOGIN_SUCCESS)
.delay(2000)
.mergeMap(({ payload: { msg } }) => showMessage(msg))
const rootEpic = combineEpics(loginRequestEpic, loginSuccessfulEpic)
A port of the Elm Architecture to Redux that allows you to sequence your effects naturally and purely by returning them from your reducers. Reducers now return both a state value and a side effect description.
Best for: trying to be as much like Elm as possible in Redux+JS
export const reducer = (state = {}, action) => {
switch (action.type) {
case ActionType.LOGIN_REQUEST:
const { username, password } = action.payload
return loop(
{ pending: true },
Effect.promise(loginPromise, username, password)
)
case ActionType.LOGIN_SUCCESS:
const { user, msg } = action.payload
return loop(
{ pending: false, user },
Effect.promise(delayMessagePromise, msg, 2000)
)
case ActionType.LOGIN_FAILURE:
return { pending: false, err: action.payload }
default:
return state
}
}
Side effects lib built with observables, but allows use of callbacks, promises, async/await, or observables. Provides declarative processing of actions.
Best for: very decoupled async logic
const loginLogic = createLogic({
type: Actions.LOGIN_REQUEST,
process({ getState, action }, dispatch, done) {
const { username, password } = action.payload
postLogin(username, password)
.then(
({ user, msg }) => {
dispatch(loginSucceeded(user))
setTimeout(() => dispatch(showMessage(msg)), 2000)
},
err => dispatch(loginFailure(err))
)
.then(done)
}
})
Promises
acdlite/redux-promise
Dispatch promises as action payloads, and have FSA-compliant actions dispatched as the promise resolves or rejects.
dispatch({ type: 'FETCH_DATA', payload: myAjaxLib.get('/data') })
// will dispatch either {type : "FETCH_DATA", payload : response} if resolved,
// or dispatch {type : "FETCH_DATA", payload : error, error : true} if rejected
lelandrichardson/redux-pack
Sensible, declarative, convention-based promise handling that guides users in a good direction without exposing the full power of dispatch.
dispatch({type : "FETCH_DATA", payload : myAjaxLib.get("/data") });
// in a reducer:
case "FETCH_DATA": =
return handle(state, action, {
start: prevState => ({
...prevState,
isLoading: true,
fooError: null
}),
finish: prevState => ({ ...prevState, isLoading: false }),
failure: prevState => ({ ...prevState, fooError: payload }),
success: prevState => ({ ...prevState, foo: payload }),
});
Middleware
Networks and Sockets
svrcekmichal/redux-axios-middleware
Fetches data with Axios and dispatches start/success/fail actions
export const loadCategories() => ({ type: 'LOAD', payload: { request : { url: '/categories'} } });
agraboso/redux-api-middleware
Reads API call actions, fetches, and dispatches FSAs
const fetchUsers = () => ({
[CALL_API]: {
endpoint: 'http://www.example.com/api/users',
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
})
itaylor/redux-socket.io
An opinionated connector between socket.io and redux.
const store = configureStore({
reducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware.concat(socketIoMiddleware)
})
store.dispatch({ type: 'server/hello', data: 'Hello!' })
tiberiuc/redux-react-firebase
Integration between Firebase, React, and Redux
Async Behavior
rt2zz/redux-action-buffer
Buffers all actions into a queue until a breaker condition is met, at which point the queue is released
wyze/redux-debounce
FSA-compliant middleware for Redux to debounce actions.
mathieudutour/redux-queue-offline
Queue actions when offline and dispatch them when getting back online.
Analytics
rangle/redux-beacon
Integrates with any analytics services, can track while offline, and decouples analytics logic from app logic
markdalgleish/redux-analytics
Watches for Flux Standard Actions with meta analytics values and processes them
Entities and Collections
tommikaikkonen/redux-orm
A simple immutable ORM to manage relational data in your Redux store.
Versent/redux-crud
Convention-based actions and reducers for CRUD logic
kwelch/entities-reducer
A higher-order reducer that handles data from Normalizr
amplitude/redux-query
Declare colocated data dependencies with your components, run queries when components mount, perform optimistic updates, and trigger server changes with Redux actions.
cantierecreativo/redux-bees
Declarative JSON-API interaction that normalizes data, with a React HOC that can run queries
GetAmbassador/redux-clerk
Async CRUD handling with normalization, optimistic updates, sync/async action creators, selectors, and an extendable reducer.
shoutem/redux-io
JSON-API abstraction with async CRUD, normalization, optimistic updates, caching, data status, and error handling.
jmeas/redux-resource
A tiny but powerful system for managing 'resources': data that is persisted to remote servers.
Component State and Encapsulation
threepointone/redux-react-local
Local component state in Redux, with handling for component actions
@local({
ident: 'counter', initial: 0, reducer : (state, action) => action.me ? state + 1 : state }
})
class Counter extends React.Component {
epeli/lean-redux
Makes component state in Redux as easy as setState
const DynamicCounters = connectLean(
scope: "dynamicCounters",
getInitialState() => ({counterCount : 1}),
addCounter, removeCounter
)(CounterList);
DataDog/redux-doghouse
Aims to make reusable components easier to build with Redux by scoping actions and reducers to a particular instance of a component.
const scopeableActions = new ScopedActionFactory(actionCreators)
const actionCreatorsScopedToA = scopeableActions.scope('a')
actionCreatorsScopedToA.foo('bar') //{ type: SET_FOO, value: 'bar', scopeID: 'a' }
const boundScopeableActions = bindScopedActionFactories(
scopeableActions,
store.dispatch
)
const scopedReducers = scopeReducers(reducers)
Dev Tools
Debuggers and Viewers
Dan Abramov's original Redux DevTools implementation, built for in-app display of state and time-travel debugging
zalmoxisus/redux-devtools-extension
Mihail Diordiev's browser extension, which bundles multiple state monitor views and adds integration with the browser's own dev tools
A cross-platform Electron app for inspecting React and React Native apps, including app state, API requests, perf, errors, sagas, and action dispatching.
DevTools Monitors
Log Monitor
The default monitor for Redux DevTools with a tree view
Dock Monitor
A resizable and movable dock for Redux DevTools monitors
Slider Monitor
A custom monitor for Redux DevTools to replay recorded Redux actions
Diff Monitor
A monitor for Redux DevTools that diffs the Redux store mutations between actions
Filterable Log Monitor
Filterable tree view monitor for Redux DevTools
Filter Actions
Redux DevTools composable monitor with the ability to filter actions
Logging
evgenyrodionov/redux-logger
Logging middleware that shows actions, states, and diffs
inakianduaga/redux-state-history
Enhancer that provides time-travel and efficient action recording capabilities, including import/export of action logs and action playback.
joshwcomeau/redux-vcr
Record and replay user sessions in real-time
socialtables/redux-unhandled-action
Warns about actions that produced no state changes in development
Mutation Detection
leoasis/redux-immutable-state-invariant
Middleware that throws an error when you try to mutate your state either inside a dispatch or between dispatches.
flexport/mutation-sentinel
Helps you deeply detect mutations at runtime and enforce immutability in your codebase.
mmahalwy/redux-pure-connect
Check and log whether react-redux's connect method is passed mapState
functions that create impure props.
Testing
arnaudbenard/redux-mock-store
A mock store that saves dispatched actions in an array for assertions
Workable/redux-test-belt
Extends the store API to make it easier assert, isolate, and manipulate the store
conorhastings/redux-test-recorder
Middleware to automatically generate reducers tests based on actions in the app
wix/redux-testkit
Complete and opinionated testkit for testing Redux projects (reducers, selectors, actions, thunks)
jfairbank/redux-saga-test-plan
Makes integration and unit testing of sagas a breeze
Routing
supasate/connected-react-router Synchronize React Router v4+ state with your Redux store.
faceyspacey/redux-first-router
Seamless Redux-first routing. Think of your app in states, not routes, not components, while keeping the address bar in sync. Everything is state. Connect your components and just dispatch flux standard actions.
Forms
erikras/redux-form
A full-featured library to enable a React HTML form to store its state in Redux.
davidkpiano/react-redux-form
React Redux Form is a collection of reducer creators and action creators that make implementing even the most complex and custom forms with React and Redux simple and performant.
Higher-Level Abstractions
keajs/kea
An abstraction over Redux, Redux-Saga and Reselect. Provides a framework for your app’s actions, reducers, selectors and sagas. It empowers Redux, making it as simple to use as setState. It reduces boilerplate and redundancy, while retaining composability.
TheComfyChair/redux-scc
Takes a defined structure and uses 'behaviors' to create a set of actions, reducer responses and selectors.
Bloomca/redux-tiles
Provides minimal abstraction on top of Redux, to allow easy composability, easy async requests, and sane testability.
Community Conventions
Flux Standard Action
A human-friendly standard for Flux action objects
Canonical Reducer Composition
An opinionated standard for nested reducer composition
Ducks: Redux Reducer Bundles
A proposal for bundling reducers, action types and actions