Implementing State Management in JavaScript with Redux
Best practices for managing application state using Redux to build predictable and maintainable JavaScript applications.
0 likes
13 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" ] } ] }