Implementing State Management in JavaScript with Redux
Best practices for managing application state using Redux to build predictable and maintainable JavaScript applications.
0 likes
169 views
Rule Content
{
"title": "Implementing State Management in JavaScript with Redux",
"description": "Best practices for managing application state using Redux to build predictable and maintainable JavaScript applications.",
"category": "JavaScript Cursor Rules",
"rules": [
{
"name": "Use Redux Toolkit for Simplified Redux Logic",
"description": "Utilize Redux Toolkit (RTK) to streamline Redux development by reducing boilerplate code and enforcing best practices.",
"recommendation": "Adopt Redux Toolkit's `createSlice` and `configureStore` functions to simplify the creation of reducers and store configuration.",
"example": {
"before": "const initialState = { value: 0 };\nfunction counterReducer(state = initialState, action) {\n switch (action.type) {\n case 'increment':\n return { ...state, value: state.value + 1 };\n default:\n return state;\n }\n}",
"after": "import { createSlice, configureStore } from '@reduxjs/toolkit';\nconst counterSlice = createSlice({\n name: 'counter',\n initialState: { value: 0 },\n reducers: {\n increment: state => { state.value += 1; }\n }\n});\nconst store = configureStore({ reducer: counterSlice.reducer });"
},
"references": [
"https://redux.js.org/style-guide/#use-redux-toolkit-for-writing-redux-logic"
]
},
{
"name": "Normalize State Shape",
"description": "Structure the Redux state in a flat, normalized manner to improve efficiency and manageability.",
"recommendation": "Use libraries like `normalizr` to transform nested data into a normalized format, reducing redundancy and simplifying state updates.",
"example": {
"before": "const state = {\n posts: [\n { id: 1, title: 'Post 1', author: { id: 1, name: 'Alice' } },\n { id: 2, title: 'Post 2', author: { id: 2, name: 'Bob' } }\n ]\n};",
"after": "const state = {\n entities: {\n posts: {\n 1: { id: 1, title: 'Post 1', authorId: 1 },\n 2: { id: 2, title: 'Post 2', authorId: 2 }\n },\n users: {\n 1: { id: 1, name: 'Alice' },\n 2: { id: 2, name: 'Bob' }\n }\n },\n result: [1, 2]\n};"
},
"references": [
"https://redux.js.org/style-guide/#normalize-state-shape"
]
},
{
"name": "Use Selectors for Accessing State",
"description": "Encapsulate state access logic using selectors to improve code maintainability and performance.",
"recommendation": "Define selectors to abstract state structure and use memoization libraries like Reselect to optimize derived state computations.",
"example": {
"before": "const mapStateToProps = state => ({\n posts: state.posts.filter(post => post.published)\n});",
"after": "import { createSelector } from 'reselect';\nconst selectPublishedPosts = createSelector(\n state => state.posts,\n posts => posts.filter(post => post.published)\n);\nconst mapStateToProps = state => ({\n posts: selectPublishedPosts(state)\n});"
},
"references": [
"https://redux.js.org/style-guide/#use-selectors-for-accessing-state"
]
},
{
"name": "Keep Reducers Pure and Side-Effect Free",
"description": "Ensure reducers are pure functions without side effects to maintain predictability and testability.",
"recommendation": "Perform side effects like API calls or routing transitions outside of reducers, typically in middleware or action creators.",
"example": {
"before": "function counterReducer(state = { value: 0 }, action) {\n switch (action.type) {\n case 'increment':\n fetch('/api/increment'); // Side effect\n return { ...state, value: state.value + 1 };\n default:\n return state;\n }\n}",
"after": "function counterReducer(state = { value: 0 }, action) {\n switch (action.type) {\n case 'increment':\n return { ...state, value: state.value + 1 };\n default:\n return state;\n }\n}\n\nfunction incrementAsync() {\n return dispatch => {\n fetch('/api/increment').then(() => {\n dispatch({ type: 'increment' });\n });\n };\n}"
},
"references": [
"https://redux.js.org/style-guide/#reducers-must-not-have-side-effects"
]
},
{
"name": "Avoid Mutating State",
"description": "Prevent state mutations to ensure application predictability and enable features like time-travel debugging.",
"recommendation": "Use libraries like Immer to write immutable update logic in a more concise and readable manner.",
"example": {
"before": "function addTodo(state, action) {\n state.todos.push(action.payload); // Mutating state\n return state;\n}",
"after": "import produce from 'immer';\nfunction addTodo(state, action) {\n return produce(state, draft => {\n draft.todos.push(action.payload);\n });\n}"
},
"references": [
"https://redux.js.org/style-guide/#do-not-mutate-state"
]
},
{
"name": "Use Middleware for Asynchronous Actions",
"description": "Handle asynchronous operations using middleware to keep reducers pure and maintainable.",
"recommendation": "Implement middleware like Redux Thunk or Redux Saga to manage side effects and asynchronous logic.",
"example": {
"before": "function fetchUser(userId) {\n return {\n type: 'FETCH_USER',\n payload: fetch(`/api/users/${userId}`).then(response => response.json())\n };\n}",
"after": "function fetchUser(userId) {\n return async dispatch => {\n dispatch({ type: 'FETCH_USER_REQUEST' });\n try {\n const response = await fetch(`/api/users/${userId}`);\n const data = await response.json();\n dispatch({ type: 'FETCH_USER_SUCCESS', payload: data });\n } catch (error) {\n dispatch({ type: 'FETCH_USER_FAILURE', error });\n }\n };\n}"
},
"references": [
"https://redux.js.org/style-guide/#use-middleware-for-async-logic"
]
},
{
"name": "Organize Code by Feature",
"description": "Structure your Redux codebase by feature to enhance scalability and maintainability.",
"recommendation": "Group related actions, reducers, and components within feature-specific directories.",
"example": {
"before": "src/\n actions/\n userActions.js\n postActions.js\n reducers/\n userReducer.js\n postReducer.js\n components/\n UserComponent.js\n PostComponent.js",
"after": "src/\n features/\n user/\n actions.js\n reducer.js\n UserComponent.js\n post/\n actions.js\n reducer.js\n PostComponent.js"
},
"references": [
"https://redux.js.org/style-guide/#structure-files-as-feature-folders-with-single-file-logic"
]
}
]
}