Redux
lastmod 2025-06-04created 2017-01-3026 edits
Redux is an open-source JavaScript library, used to manage the state of a website or application using state.
Categories
- redux-persist
- redux-logger
- Redux Toolkit (redux-toolkit)
- redux-thunk - The middleware most commonly used to handle asynchronous work in Redux. With this middleware, you can dispatch functions instead of action objects.
- Pros: lets you dispatch several things at once
- Cons: that's all it does; you have to implement everything else yourself
Pros of Redux
- Makes state predictable. (because it uses pure functions)
- Maintainability (compared with complex state management)
- Good for debugging (when logging actions and state) → redux dev tool (Chrome extension)
- Easy to add tests (because it uses pure functions)
Basic concepts of Redux: three principles
- Single source of truth
- The same data always comes from the same place.
- That is, there is a single data space called the store.
- State is read-only
- In React, state can only be changed using the
setStatemethod. - In Redux too, state can only be changed through objects called actions.
- Changes are made with pure functions
- Changes can only be made with pure functions.
- This concept is related to reducers.
- Store – Action – Reducer
Meaning and characteristics of Store, Action, and Reducer
Redux-state-step.png
This is because data must flow in only one direction.
Store
- The Store is the one and only place where state is managed.
- There is a space called the store, separate from components, and the state the app needs is kept in that store.
- Components access the store when they need state information.
Action
- An Action is the data the app carries to the store. (an order form)
- An Action takes the form of a JavaScript object.
Reducer
- An Action is not passed directly to the Store.
- The Action must be passed to the Reducer.
- The Reducer looks at the order and updates the state of the Store.
- To pass an Action to the Reducer, you must use the
dispatch()method.
Two ways to implement the above concepts in Redux
-
mapStateToProps() - Redux hooks
- useSelector
- useDispatch
Installation for use with React
Provider
import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
import './index.css';
// Redux 관련 불러오기
import { createStore } from 'redux'
import reducers from './reducers';
import { Provider } from 'react-redux';
// 스토어 생성
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Deprecated Example
| WARNING |
| This is deprecated, so use #@reduxjs/toolkit Example instead |
import { createStore } from 'redux'
/**
* 이것이 (state, action) => state 형태의 순수 함수인 리듀서입니다.
* 리듀서는 액션이 어떻게 상태를 다음 상태로 변경하는지 서술합니다.
*
* 상태의 모양은 당신 마음대로입니다: 기본형(primitive)일수도, 배열일수도, 객체일수도,
* 심지어 Immutable.js 자료구조일수도 있습니다. 오직 중요한 점은 상태 객체를 변경해서는 안되며,
* 상태가 바뀐다면 새로운 객체를 반환해야 한다는 것입니다.
*
* 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
* 여러분의 프로젝트에 맞게
* (함수 맵 같은) 다른 컨벤션을 따르셔도 좋습니다.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 앱의 상태를 보관하는 Redux 저장소를 만듭니다.
// API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)
// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 현재 상태를 localStorage에 영속적으로 저장할 때도 편리합니다.
store.subscribe(() => console.log(store.getState())))
// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
@reduxjs/toolkit Example
- Using actions and reducers simply with createSlice (redux-toolkit)
- Asynchronous processing with Redux Toolkit's createAsyncThunk
- [Recommended] maruzzing's devlog - State management with Redux-toolkit 1
- [Recommended] (6/5) Setting up Redux-toolkit + tutorial translation (Next.js 13)
Use it as follows: app/store.js:
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
},
});
features/counter/counterSlice.js:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { fetchCount } from './counterAPI';
const initialState = {
value: 0,
status: 'idle',
};
// The function below is called a thunk and allows us to perform async logic. It
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
// will call the thunk with the `dispatch` function as the first argument. Async
// code can then be executed and other actions can be dispatched. Thunks are
// typically used to make async requests.
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
async (amount) => {
const response = await fetchCount(amount);
// The value we return becomes the `fulfilled` action payload
return response.data;
}
);
export const counterSlice = createSlice({
name: 'counter',
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
// The `extraReducers` field lets the slice handle actions defined elsewhere,
// including actions generated by createAsyncThunk or in other slices.
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = 'loading';
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'idle';
state.value += action.payload;
});
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
export const selectCount = (state) => state.counter.value;
// We can also write thunks by hand, which may contain both sync and async logic.
// Here's an example of conditionally dispatching actions based on current state.
export const incrementIfOdd = (amount) => (dispatch, getState) => {
const currentValue = selectCount(getState());
if (currentValue % 2 === 1) {
dispatch(incrementByAmount(amount));
}
};
export default counterSlice.reducer;
features/counter/counterAPI.js:
// A mock function to mimic making an async request for data
export function fetchCount(amount = 1) {
return new Promise((resolve) =>
setTimeout(() => resolve({ data: amount }), 500)
);
}
index.js:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './index.css';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
Usage example:
import { useSelector, useDispatch } from 'react-redux';
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
incrementIfOdd,
selectCount,
} from './counterSlice';
export function Counter() {
const count = useSelector(selectCount);
const dispatch = useDispatch();
dispatch(decrement());
dispatch(increment());
dispatch(incrementByAmount(2));
// ...
}
See also
Favorite site
- Redux (JavaScript library) - Wikipedia
- What is Redux? (state management library) - Hanamon
- Thoughts on designing Redux applications
- Making sense of Redux
Comparison of state management libraries
- Comparing the latest React state management libraries (feat. zustand, redux-toolkit, jotai, recoil) :: Dev Notepad (zustand, redux-toolkit, jotai, recoil)
- State management library comparison: Redux vs Recoil vs Zustand vs Jotai (redux, recoil, zustand, jotai)
References
-
WebFrameworks_-_Making_sense_of_Redux.pdf ↩
Pages that link here