{"id":16073719,"url":"https://github.com/ashmalzahra/redux-toolkit-tutorial","last_synced_at":"2025-04-05T10:26:50.769Z","repository":{"id":162516157,"uuid":"607322103","full_name":"ashmalzahra/redux-toolkit-tutorial","owner":"ashmalzahra","description":"This Project is about practicing the concepts relating to - creating a store, start defining and dispatching actions, and reducers.","archived":false,"fork":false,"pushed_at":"2023-02-28T04:06:58.000Z","size":316,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"develop","last_synced_at":"2025-02-10T21:19:46.016Z","etag":null,"topics":["reactjs","redux","redux-thunk","tutorial"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ashmalzahra.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-27T18:58:28.000Z","updated_at":"2023-03-24T18:05:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"d420d50d-7ff0-4f7b-9068-0b882d19ed3e","html_url":"https://github.com/ashmalzahra/redux-toolkit-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmalzahra%2Fredux-toolkit-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmalzahra%2Fredux-toolkit-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmalzahra%2Fredux-toolkit-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmalzahra%2Fredux-toolkit-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ashmalzahra","download_url":"https://codeload.github.com/ashmalzahra/redux-toolkit-tutorial/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247321446,"owners_count":20919999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["reactjs","redux","redux-thunk","tutorial"],"created_at":"2024-10-09T08:22:31.113Z","updated_at":"2025-04-05T10:26:50.752Z","avatar_url":"https://github.com/ashmalzahra.png","language":null,"readme":"# Redux Toolkit\n\n#### React Course\n\n[My React Course](https://www.udemy.com/course/react-tutorial-and-projects-course/?referralCode=FEE6A921AF07E2563CEF)\n\n#### Support\n\nFind the App Useful? [You can always buy me a coffee](https://www.buymeacoffee.com/johnsmilga)\n\n#### Docs\n\n[Redux Toolkit Docs](https://redux-toolkit.js.org/introduction/getting-started)\n\n#### Install Template\n\n```sh\nnpx create-react-app my-app --template redux\n```\n\n- @latest\n\n```sh\nnpx create-react-app@latest my-app --template redux\n```\n\n#### Existing App\n\n```sh\nnpm install @reduxjs/toolkit react-redux\n```\n\n#### @reduxjs/toolkit\n\nconsists of few libraries\n\n- redux (core library, state management)\n- immer (allows to mutate state)\n- redux-thunk (handles async actions)\n- reselect (simplifies reducer functions)\n\n#### Extras\n\n- redux devtools\n- combine reducers\n\n#### react-redux\n\nconnects our app to redux\n\n#### Setup Store\n\n- create store.js\n\n```js\nimport { configureStore } from '@reduxjs/toolkit';\n\nexport const store = configureStore({\n  reducer: {},\n});\n```\n\n#### Setup Provider\n\n- index.js\n\n```js\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\n// import store and provider\nimport { store } from './store';\nimport { Provider } from 'react-redux';\n\nReactDOM.render(\n  \u003cReact.StrictMode\u003e\n    \u003cProvider store={store}\u003e\n      \u003cApp /\u003e\n    \u003c/Provider\u003e\n  \u003c/React.StrictMode\u003e,\n  document.getElementById('root')\n);\n```\n\n#### Setup Cart Slice\n\n- application feature\n- create features folder/cart\n- create cartSlice.js\n\n```js\nimport { createSlice } from '@reduxjs/toolkit';\n\nconst initialState = {\n  cartItems: [],\n  amount: 0,\n  total: 0,\n  isLoading: true,\n};\n\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState,\n});\n\nconsole.log(cartSlice);\n\nexport default cartSlice.reducer;\n```\n\n- store.js\n\n```js\nimport { configureStore } from '@reduxjs/toolkit';\nimport cartReducer from './features/cart/cartSlice';\n\nexport const store = configureStore({\n  reducer: {\n    cart: cartReducer,\n  },\n});\n```\n\n#### Redux DevTools\n\n- extension\n\n#### Access store value\n\n- create components/Navbar.js\n\n```js\nimport { CartIcon } from '../icons';\nimport { useSelector } from 'react-redux';\n\nconst Navbar = () =\u003e {\n  const { amount } = useSelector((state) =\u003e state.cart);\n\n  return (\n    \u003cnav\u003e\n      \u003cdiv className='nav-center'\u003e\n        \u003ch3\u003eredux toolkit\u003c/h3\u003e\n        \u003cdiv className='nav-container'\u003e\n          \u003cCartIcon /\u003e\n          \u003cdiv className='amount-container'\u003e\n            \u003cp className='total-amount'\u003e{amount}\u003c/p\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/nav\u003e\n  );\n};\nexport default Navbar;\n```\n\n#### Hero Icons\n\n- [Hero Icons](https://heroicons.com/)\n\n```css\nnav svg {\n  width: 40px;\n  color: var(--clr-white);\n}\n```\n\n#### Setup Cart\n\n- cartSlice.js\n\n```js\nimport cartItems from '../../cartItems';\n\nconst initialState = {\n  cartItems: cartItems,\n  amount: 0,\n  total: 0,\n  isLoading: true,\n};\n```\n\n- create CartContainer.js and CartItem.js\n- CartContainer.js\n\n```js\nimport React from 'react';\nimport CartItem from './CartItem';\nimport { useSelector } from 'react-redux';\n\nconst CartContainer = () =\u003e {\n  const { cartItems, total, amount } = useSelector((state) =\u003e state.cart);\n\n  if (amount \u003c 1) {\n    return (\n      \u003csection className='cart'\u003e\n        {/* cart header */}\n        \u003cheader\u003e\n          \u003ch2\u003eyour bag\u003c/h2\u003e\n          \u003ch4 className='empty-cart'\u003eis currently empty\u003c/h4\u003e\n        \u003c/header\u003e\n      \u003c/section\u003e\n    );\n  }\n  return (\n    \u003csection className='cart'\u003e\n      {/* cart header */}\n      \u003cheader\u003e\n        \u003ch2\u003eyour bag\u003c/h2\u003e\n      \u003c/header\u003e\n      {/* cart items */}\n      \u003cdiv\u003e\n        {cartItems.map((item) =\u003e {\n          return \u003cCartItem key={item.id} {...item} /\u003e;\n        })}\n      \u003c/div\u003e\n      {/* cart footer */}\n      \u003cfooter\u003e\n        \u003chr /\u003e\n        \u003cdiv className='cart-total'\u003e\n          \u003ch4\u003e\n            total \u003cspan\u003e${total}\u003c/span\u003e\n          \u003c/h4\u003e\n        \u003c/div\u003e\n        \u003cbutton className='btn clear-btn'\u003eclear cart\u003c/button\u003e\n      \u003c/footer\u003e\n    \u003c/section\u003e\n  );\n};\n\nexport default CartContainer;\n```\n\n- CartItem.js\n\n```js\nimport React from 'react';\nimport { ChevronDown, ChevronUp } from '../icons';\n\nconst CartItem = ({ id, img, title, price, amount }) =\u003e {\n  return (\n    \u003carticle className='cart-item'\u003e\n      \u003cimg src={img} alt={title} /\u003e\n      \u003cdiv\u003e\n        \u003ch4\u003e{title}\u003c/h4\u003e\n        \u003ch4 className='item-price'\u003e${price}\u003c/h4\u003e\n        {/* remove button */}\n        \u003cbutton className='remove-btn'\u003eremove\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        {/* increase amount */}\n        \u003cbutton className='amount-btn'\u003e\n          \u003cChevronUp /\u003e\n        \u003c/button\u003e\n        {/* amount */}\n        \u003cp className='amount'\u003e{amount}\u003c/p\u003e\n        {/* decrease amount */}\n        \u003cbutton className='amount-btn'\u003e\n          \u003cChevronDown /\u003e\n        \u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/article\u003e\n  );\n};\n\nexport default CartItem;\n```\n\n#### First Reducer\n\n- cartSlice.js\n- Immer library\n\n```js\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState,\n  reducers: {\n    clearCart: (state) =\u003e {\n      state.cartItems = [];\n    },\n  },\n});\n\nexport const { clearCart } = cartSlice.actions;\n```\n\n- create action\n\n```js\nconst ACTION_TYPE = 'ACTION_TYPE';\n\nconst actionCreator = (payload) =\u003e {\n  return { type: ACTION_TYPE, payload: payload };\n};\n```\n\n- CartContainer.js\n\n```js\nimport React from 'react';\nimport CartItem from './CartItem';\nimport { useDispatch, useSelector } from 'react-redux';\n\nconst CartContainer = () =\u003e {\n  const dispatch = useDispatch();\n\n  return (\n    \u003cbutton\n      className='btn clear-btn'\n      onClick={() =\u003e {\n        dispatch(clearCart());\n      }}\n    \u003e\n      clear cart\n    \u003c/button\u003e\n  );\n};\n\nexport default CartContainer;\n```\n\n#### Remove, Increase, Decrease\n\n- cartSlice.js\n\n```js\nimport { createSlice } from '@reduxjs/toolkit';\nimport cartItems from '../../cartItems';\n\nconst initialState = {\n  cartItems: [],\n  amount: 0,\n  total: 0,\n  isLoading: true,\n};\n\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState,\n  reducers: {\n    clearCart: (state) =\u003e {\n      state.cartItems = [];\n    },\n    removeItem: (state, action) =\u003e {\n      const itemId = action.payload;\n      state.cartItems = state.cartItems.filter((item) =\u003e item.id !== itemId);\n    },\n    increase: (state, { payload }) =\u003e {\n      const cartItem = state.cartItems.find((item) =\u003e item.id === payload.id);\n      cartItem.amount = cartItem.amount + 1;\n    },\n    decrease: (state, { payload }) =\u003e {\n      const cartItem = state.cartItems.find((item) =\u003e item.id === payload.id);\n      cartItem.amount = cartItem.amount - 1;\n    },\n    calculateTotals: (state) =\u003e {\n      let amount = 0;\n      let total = 0;\n      state.cartItems.forEach((item) =\u003e {\n        amount += item.amount;\n        total += item.amount * item.price;\n      });\n      state.amount = amount;\n      state.total = total;\n    },\n  },\n});\n\nexport const { clearCart, removeItem, increase, decrease, calculateTotals } =\n  cartSlice.actions;\n\nexport default cartSlice.reducer;\n```\n\n- CartItem.js\n\n```js\nimport React from 'react';\nimport { ChevronDown, ChevronUp } from '../icons';\n\nimport { useDispatch } from 'react-redux';\nimport { removeItem, increase, decrease } from '../features/cart/cartSlice';\n\nconst CartItem = ({ id, img, title, price, amount }) =\u003e {\n  const dispatch = useDispatch();\n\n  return (\n    \u003carticle className='cart-item'\u003e\n      \u003cimg src={img} alt={title} /\u003e\n      \u003cdiv\u003e\n        \u003ch4\u003e{title}\u003c/h4\u003e\n        \u003ch4 className='item-price'\u003e${price}\u003c/h4\u003e\n        {/* remove button */}\n        \u003cbutton\n          className='remove-btn'\n          onClick={() =\u003e {\n            dispatch(removeItem(id));\n          }}\n        \u003e\n          remove\n        \u003c/button\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        {/* increase amount */}\n        \u003cbutton\n          className='amount-btn'\n          onClick={() =\u003e {\n            dispatch(increase({ id }));\n          }}\n        \u003e\n          \u003cChevronUp /\u003e\n        \u003c/button\u003e\n        {/* amount */}\n        \u003cp className='amount'\u003e{amount}\u003c/p\u003e\n        {/* decrease amount */}\n        \u003cbutton\n          className='amount-btn'\n          onClick={() =\u003e {\n            if (amount === 1) {\n              dispatch(removeItem(id));\n              return;\n            }\n            dispatch(decrease({ id }));\n          }}\n        \u003e\n          \u003cChevronDown /\u003e\n        \u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/article\u003e\n  );\n};\n\nexport default CartItem;\n```\n\n- App.js\n\n```js\nimport { useEffect } from 'react';\nimport Navbar from './components/Navbar';\nimport CartContainer from './components/CartContainer';\nimport { useSelector, useDispatch } from 'react-redux';\nimport { calculateTotals } from './features/cart/cartSlice';\n\nfunction App() {\n  const { cartItems } = useSelector((state) =\u003e state.cart);\n  const dispatch = useDispatch();\n  useEffect(() =\u003e {\n    dispatch(calculateTotals());\n  }, [cartItems]);\n\n  return (\n    \u003cmain\u003e\n      \u003cNavbar /\u003e\n      \u003cCartContainer /\u003e\n    \u003c/main\u003e\n  );\n}\n\nexport default App;\n```\n\n#### Modal\n\n- create components/Modal.js\n\n```js\nconst Modal = () =\u003e {\n  return (\n    \u003caside className='modal-container'\u003e\n      \u003cdiv className='modal'\u003e\n        \u003ch4\u003eRemove all items from your shopping cart?\u003c/h4\u003e\n        \u003cdiv className='btn-container'\u003e\n          \u003cbutton type='button' className='btn confirm-btn'\u003e\n            confirm\n          \u003c/button\u003e\n          \u003cbutton type='button' className='btn clear-btn'\u003e\n            cancel\n          \u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/aside\u003e\n  );\n};\nexport default Modal;\n```\n\n- App.js\n\n```js\nreturn (\n  \u003cmain\u003e\n    \u003cModal /\u003e\n    \u003cNavbar /\u003e\n    \u003cCartContainer /\u003e\n  \u003c/main\u003e\n);\n```\n\n#### modal slice\n\n- create features/modal/modalSlice.js\n\n```js\nimport { createSlice } from '@reduxjs/toolkit';\nconst initialState = {\n  isOpen: false,\n};\n\nconst modalSlice = createSlice({\n  name: 'modal',\n  initialState,\n  reducers: {\n    openModal: (state, action) =\u003e {\n      state.isOpen = true;\n    },\n    closeModal: (state, action) =\u003e {\n      state.isOpen = false;\n    },\n  },\n});\n\nexport const { openModal, closeModal } = modalSlice.actions;\nexport default modalSlice.reducer;\n```\n\n- App.js\n\n```js\nconst { isOpen } = useSelector((state) =\u003e state.modal);\n\nreturn (\n  \u003cmain\u003e\n    {isOpen \u0026\u0026 \u003cModal /\u003e}\n    \u003cNavbar /\u003e\n    \u003cCartContainer /\u003e\n  \u003c/main\u003e\n);\n```\n\n#### toggle modal\n\n- CartContainer.js\n\n```js\nimport { openModal } from '../features/modal/modalSlice';\n\nreturn (\n  \u003cbutton\n    className='btn clear-btn'\n    onClick={() =\u003e {\n      dispatch(openModal());\n    }}\n  \u003e\n    clear cart\n  \u003c/button\u003e\n);\n```\n\n- Modal.js\n\n```js\nimport { closeModal } from '../features/modal/modalSlice';\nimport { useDispatch } from 'react-redux';\nimport { clearCart } from '../features/cart/cartSlice';\n\nconst Modal = () =\u003e {\n  const dispatch = useDispatch();\n\n  return (\n    \u003caside className='modal-container'\u003e\n      \u003cdiv className='modal'\u003e\n        \u003ch4\u003eRemove all items from your shopping cart?\u003c/h4\u003e\n        \u003cdiv className='btn-container'\u003e\n          \u003cbutton\n            type='button'\n            className='btn confirm-btn'\n            onClick={() =\u003e {\n              dispatch(clearCart());\n              dispatch(closeModal());\n            }}\n          \u003e\n            confirm\n          \u003c/button\u003e\n          \u003cbutton\n            type='button'\n            className='btn clear-btn'\n            onClick={() =\u003e {\n              dispatch(closeModal());\n            }}\n          \u003e\n            cancel\n          \u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/aside\u003e\n  );\n};\nexport default Modal;\n```\n\n#### async functionality with createAsyncThunk\n\n- [Course API](https://course-api.com/)\n- https://course-api.com/react-useReducer-cart-project\n- cartSlice.js\n\n- action type\n- callback function\n- lifecycle actions\n\n```js\nimport { createSlice, createAsyncThunk } from '@reduxjs/toolkit';\n\nconst url = 'https://course-api.com/react-useReducer-cart-project';\n\nexport const getCartItems = createAsyncThunk('cart/getCartItems', () =\u003e {\n  return fetch(url)\n    .then((resp) =\u003e resp.json())\n    .catch((err) =\u003e console.log(error));\n});\n\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState,\n  extraReducers: {\n    [getCartItems.pending]: (state) =\u003e {\n      state.isLoading = true;\n    },\n    [getCartItems.fulfilled]: (state, action) =\u003e {\n      console.log(action);\n      state.isLoading = false;\n      state.cartItems = action.payload;\n    },\n    [getCartItems.rejected]: (state) =\u003e {\n      state.isLoading = false;\n    },\n  },\n});\n```\n\n- App.js\n\n```js\nimport { calculateTotals, getCartItems } from './features/cart/cartSlice';\n\nfunction App() {\n  const { cartItems, isLoading } = useSelector((state) =\u003e state.cart);\n\n  useEffect(() =\u003e {\n    dispatch(getCartItems());\n  }, []);\n\n  if (isLoading) {\n    return (\n      \u003cdiv className='loading'\u003e\n        \u003ch1\u003eLoading...\u003c/h1\u003e\n      \u003c/div\u003e\n    );\n  }\n\n  return (\n    \u003cmain\u003e\n      {isOpen \u0026\u0026 \u003cModal /\u003e}\n      \u003cNavbar /\u003e\n      \u003cCartContainer /\u003e\n    \u003c/main\u003e\n  );\n}\n\nexport default App;\n```\n\n#### Options\n\n```sh\nnpm install axios\n```\n\n- cartSlice.js\n\n```js\nexport const getCartItems = createAsyncThunk(\n  'cart/getCartItems',\n  async (name, thunkAPI) =\u003e {\n    try {\n      // console.log(name);\n      // console.log(thunkAPI);\n      // console.log(thunkAPI.getState());\n      // thunkAPI.dispatch(openModal());\n      const resp = await axios(url);\n\n      return resp.data;\n    } catch (error) {\n      return thunkAPI.rejectWithValue('something went wrong');\n    }\n  }\n);\n```\n\n#### The extraReducers \"builder callback\" notation\n\ncart/cartSlice\n\n```js\nconst cartSlice = createSlice({\n  name: 'cart',\n  initialState,\n  reducers: {\n    // reducers\n  },\n  extraReducers: (builder) =\u003e {\n    builder\n      .addCase(getCartItems.pending, (state) =\u003e {\n        state.isLoading = true;\n      })\n      .addCase(getCartItems.fulfilled, (state, action) =\u003e {\n        // console.log(action);\n        state.isLoading = false;\n        state.cartItems = action.payload;\n      })\n      .addCase(getCartItems.rejected, (state, action) =\u003e {\n        console.log(action);\n        state.isLoading = false;\n      });\n  },\n});\n```","funding_links":["https://www.buymeacoffee.com/johnsmilga"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashmalzahra%2Fredux-toolkit-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fashmalzahra%2Fredux-toolkit-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashmalzahra%2Fredux-toolkit-tutorial/lists"}