1"use strict";(globalThis.webpackChunkmikes_dev_notebook=globalThis.webpackChunkmikes_dev_notebook||[]).push([[2699],{678722(e,n,t){t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>l});const s=JSON.parse('{"id":"Development/Javascript/2023-12-18-reactjs-typescript-redux-toolkit/index","title":"Redux Toolkit Github API","description":"Using the Redux Toolkit and AsyncThunk with Typescript & React","source":"@site/docs/Development/Javascript/2023-12-18-reactjs-typescript-redux-toolkit/index.md","sourceDirName":"Development/Javascript/2023-12-18-reactjs-typescript-redux-toolkit","slug":"/Development/Javascript/2023-12-18-reactjs-typescript-redux-toolkit/2023-12-18","permalink":"/docs/Development/Javascript/2023-12-18-reactjs-typescript-redux-toolkit/2023-12-18","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/docs/Development/Javascript/2023-12-18-reactjs-typescript-redux-toolkit/index.md","tags":[{"inline":true,"label":"Javascript","permalink":"/docs/tags/javascript"},{"inline":true,"label":"React","permalink":"/docs/tags/react"}],"version":"current","sidebarPosition":5892,"frontMatter":{"sidebar_position":5892,"slug":"2023-12-18","title":"Redux Toolkit Github API","authors":"mpolinowski","tags":["Javascript","React"],"description":"Using the Redux Toolkit and AsyncThunk with Typescript & React"},"sidebar":"tutorialSidebar","previous":{"title":"React Query AsyncState Management","permalink":"/docs/Development/Javascript/2023-12-19-react-query-typescript/2023-12-19"},"next":{"title":"React Hooks Typescript","permalink":"/docs/Development/Javascript/2023-12-16-reactjs-hooks-typescript/2023-12-16"}}');var r=t(474848),i=t(28453);const o={sidebar_position:5892,slug:"2023-12-18",title:"Redux Toolkit Github API",authors:"mpolinowski",tags:["Javascript","React"],description:"Using the Redux Toolkit and AsyncThunk with Typescript & React"},a=void 0,c={},l=[{value:"Scaffolding",id:"scaffolding",level:2},{value:"Project Tracker",id:"project-tracker",level:2},{value:"Layout",id:"layout",level:3},{value:"Redux Store",id:"redux-store",level:3},{value:"Adding the Store",id:"adding-the-store",level:4},{value:"AsyncThunk API Calls",id:"asyncthunk-api-calls",level:3},{value:"Adding the API Call",id:"adding-the-api-call",level:4}];function p(e){const n={a:"a",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",img:"img",li:"li",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"TST, Hongkong",src:t(376442).A+"",width:"2359",height:"864"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"#scaffolding",children:"Scaffolding"})}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"#project-tracker",children:"Project Tracker"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"#layout",children:"Layout"})}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"#redux-store",children:"Redux Store"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"#adding-the-store",children:"Adding the Store"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"#asyncthunk-api-calls",children:"AsyncThunk API Calls"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.a,{href:"#adding-the-api-call",children:"Adding the API Call"})}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"scaffolding",children:"Scaffolding"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm create vite@latest redux-toolkit\n\u2714 Select a framework: \u203a React\n\u2714 Select a variant: \u203a TypeScript + SWC\n"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"cd redux-toolkit\nnpm install\nnpm install @mui/material @emotion/react @emotion/styled @fontsource/roboto \nnpm install @reduxjs/toolkit react-redux @types/react-redux @types/webpack-env\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Use ",(0,r.jsx)(n.code,{children:"baseURL"})," to be able to work with absolute imports relative to the defined base:"]}
1),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./tsconfig.json"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n "compilerOptions": {\n "baseUrl": "./src",\n ...\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.em,{children:"./vite.config.js"})," ",(0,r.jsx)(n.code,{children:"npm install vite-tsconfig-paths"})]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-js",children:"import { defineConfig } from 'vite'\nimport tsconfigPaths from 'vite-tsconfig-paths'\nimport react from '@vitejs/plugin-react-swc'\n\nexport default defineConfig({\n plugins: [\n react(),\n tsconfigPaths()\n ],\n})\n"})}),"\n",(0,r.jsxs)(n.p,{children:["_",(0,r.jsx)(n.em,{children:"./src/pages/App.tsx"})]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:"import Box from '@mui/material/Box';\nimport HelloWorld from 'components/HelloWorld'\n\nimport 'styles/App.css'\n\nexport default function App() {\n \n return (\n <>\n <Box sx={{ width: '100%', maxWidth: 1000 }}>\n <HelloWorld greeting='Hello from React Typescript' />\n </Box>\n </>\n )\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:["_",(0,r.jsx)(n.em,{children:"./src/components/HelloWorld.tsx"})]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:"import React from 'react'\nimport Typography from '@mui/material/Typography';\n\nimport { iHelloProps } from 'types/interfaces'\n\nexport default function HelloWorld({ greeting }: iHelloProps): React.JSX.Element {\n return (\n <Typography variant=\"h1\" gutterBottom>\n { greeting }\n </Typography>\n )\n}\n"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm run dev\n"})}),"\n",(0,r.jsx)(n.h2,{id:"project-tracker",children:"Project Tracker"}),"\n",(0,r.jsx)(n.h3,{id:"layout",children:"Layout"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/components/ProjectCard.tsx"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:'import React from "react"\nimport { Typography, Grid, Stack, Paper} from "@mui/material"\n\ninterface IProps {\n issueTitle: string\n}\n\nexport default function ProjectCard({ issueTitle }: IProps): React.JSX.Element {\n return(\n <div className="project_card">\n <Paper elevation={1} sx={{p: \'10px\', m:\'1rem\'}}>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <Stack spacing={2}>\n <Typography variant="h6" sx={{fontWeight: \'bold\'}}>\n Issue Title: {issueTitle}\n </Typography>\n <Stack direction=\'row\' spacing={2}>\n <Typography variant="body1">\n Opened: yesterday\n </Typography>\n <Typography variant="body1">\n Priority: medium\n </Typography>\n </Stack>\n </Stack>\n </Grid>\n </Grid>\n </Paper>\n </div>\n )\n}\n'})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/pages/App.tsx"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:'import React, { useState } from "react"\nimport { Box, Typography, TextField, Stack, Button } from "@mui/material"\n\nimport ProjectCard from "components/ProjectCard"\nimport HelloWorld from \'components/HelloWorld\'\n\nimport \'styles/App.css\'\n\n\nconst App = (): React.JSX.Element => {\n const [textInput, setTextInput] = useState(\'\');\n const handleTextInputChange = (e:any) => {\n setTextInput(e.target.value);\n };\n return(\n <div className="home_page">\n <Box sx={{ml: \'5rem\', mr: \'5rem\'}}>\n <HelloWorld greeting=\'Hello from React Typescript\' />\n <Typography variant="h4" sx={{textAlign: \'center\'}}>\n Project Issue Tracker\n </Typography>\n <Box sx={{\n display: \'flex\',\n justifyContent: \'center\'\n }}>\n <Stack spacing={2}>\n <Typography variant="h5">\n Add new issue\n </Typography>\n <TextField \n id="outlined-basic" \n label="Title" \n variant="outlined" \n onChange={handleTextInputChange}\n value={textInput}\n />\n <Button variant="contained">Submit</Button>\n </Stack>\n </Box>\n <Box sx={{ml: \'1rem\', mt: \'3rem\'}}>\n <Typography variant="h5" >\n Opened issue\n </Typography>\n <ProjectCard issueTitle="Bug: Issue 1" />\n <ProjectCard issueTitle="Bug: Issue 2" />\n </Box>\n </Box>\n </div>\n )\n}\nexport default App\n'})}),"\n",(0,r.jsx)(n.h3,{id:"redux-store",children:"Redux Store"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/redux/issueReducer.ts"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"import { createSlice, PayloadAction } from \"@reduxjs/toolkit\"\n\nimport { IssueInitialState } from 'types/interfaces'\n\nconst initialState: IssueInitialState = {\n projectIssues: []\n}\n\n\nexport const issueSlice = createSlice({\n name: 'issue',\n initialState,\n reducers: {\n addIssue: (state, action: PayloadAction<string>) =>
1 {\n state.projectIssues = [...state.projectIssues, action.payload]\n }\n }\n})\n\n\nexport const { addIssue } = issueSlice.actions\nexport default issueSlice.reducer\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/redux/store.ts"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"import { configureStore } from '@reduxjs/toolkit'\n\nimport IssueReducer from \"redux/issueReducer\"\n\nexport const store = configureStore({\n reducer: {\n issue: IssueReducer\n }\n})\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/main.tsx"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { Provider } from \"react-redux\"\nimport { store } from 'redux/store';\n\nimport App from 'pages/App.tsx'\nimport 'styles/index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <Provider store={store}>\n <App />\n </Provider>\n </React.StrictMode>,\n)\n"})}),"\n",(0,r.jsx)(n.h4,{id:"adding-the-store",children:"Adding the Store"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:'import React, { useState } from "react"\nimport { Box, Typography, TextField, Stack, Button } from "@mui/material"\n\nimport ProjectCard from "components/ProjectCard"\nimport HelloWorld from \'components/HelloWorld\'\n\nimport { useDispatch, useSelector } from "react-redux"\nimport { RootState } from "redux/index"\nimport { addIssue } from "redux/issueReducer";\n\nimport \'styles/App.css\'\n\n\nconst App = (): React.JSX.Element => {\n const [textInput, setTextInput] = useState(\'\')\n\n const dispatch = useDispatch()\n const issueList = useSelector((state: RootState) => state.issue.projectIssues)\n\n const handleTextInputChange = (e:any) => {\n setTextInput(e.target.value)\n }\n\n const handleClick = () => {\n console.log(textInput)\n dispatch(addIssue(textInput))\n setTextInput(\'\')\n }\n\n return(\n <div className="home_page">\n <Box sx={{ml: \'5rem\', mr: \'5rem\'}}>\n <HelloWorld greeting=\'Hello from React Typescript\' />\n <Typography variant="h4" sx={{textAlign: \'center\'}}>\n Project Issue Tracker\n </Typography>\n <Box sx={{\n display: \'flex\',\n justifyContent: \'center\'\n }}>\n <Stack spacing={2}>\n <Typography variant="h5">\n Add new issue\n </Typography>\n <TextField \n id="outlined-basic" \n label="Title" \n variant="outlined" \n onChange={handleTextInputChange}\n value={textInput}\n />\n <Button variant="contained" onClick={handleClick}>Submit</Button>\n </Stack>\n </Box>\n <Box sx={{ml: \'1rem\', mt: \'3rem\'}}>\n <Typography variant="h5" >\n Opened issue\n </Typography>\n {\n issueList.map((issue) => {\n return(\n <ProjectCard key={issue} issueTitle={issue} />\n )\n })\n }\n </Box>\n </Box>\n </div>\n )\n}\n\nexport default App\n'})}),"\n",(0,r.jsx)(n.h3,{id:"asyncthunk-api-calls",children:"AsyncThunk API Calls"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/redux/ghIssueReducer.ts"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nimport { IssuesState }
1 from 'types/interfaces'\n\nexport const fetchIssues = createAsyncThunk<string[], void, { rejectValue: string }>(\n \"githubIssue/fetchIssues\",\n async (_, thunkAPI) => {\n try {\n const response = await fetch(\"https://api.github.com/repos/github/hub/issues\");\n const data = await response.json();\n const issues = data.map((issue: { title: string }) => issue.title);\n return issues;\n } catch (error) {\n return thunkAPI.rejectWithValue(\"Failed to fetch issues.\");\n }\n }\n)\n\nconst initialState: IssuesState = {\n issues: [],\n loading: false,\n error: null,\n}\n\nexport const issuesSliceGithub = createSlice({\n name: 'github_issues',\n initialState,\n reducers: {},\n extraReducers: (builder) => {\n builder\n .addCase(fetchIssues.pending, (state) => {\n state.loading = true;\n state.error = null;\n })\n .addCase(fetchIssues.fulfilled, (state, action) => {\n state.loading = false;\n state.issues = action.payload;\n })\n .addCase(fetchIssues.rejected, (state, action) => {\n state.loading = false;\n state.error = action.error.message || 'Something went wrong';\n });\n },\n})\n\nexport default issuesSliceGithub.reducer\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.em,{children:"./src/redux/store.ts"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"import { configureStore } from '@reduxjs/toolkit'\nimport { useDispatch } from 'react-redux'\n\nimport IssueReducer from \"redux/issueReducer\"\nimport GithubIssueReducer from 'redux/ghIssueReducer'\n\nexport const store = configureStore({\n reducer: {\n issue: IssueReducer,\n githubIssue: GithubIssueReducer\n }\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\nexport const useAppDispatch = () => useDispatch<AppDispatch>()\n"})}),"\n",(0,r.jsx)(n.h4,{id:"adding-the-api-call",children:"Adding the API Call"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-tsx",children:"import React, { useState, useEffect } from 'react'\nimport { useSelector } from 'react-redux'\nimport { Box, Typography, TextField, Stack, Button } from '@mui/material'\n\nimport ProjectCard from 'components/ProjectCard'\nimport HelloWorld from 'components/HelloWorld'\n\n\nimport { useAppDispatch, RootState, AppDispatch } from 'redux/store'\nimport { addIssue } from 'redux/issueReducer'\nimport { fetchIssues } from 'redux/ghIssueReducer'\n\nimport 'styles/App.css'\n\n\nconst App = (): React.JSX.Element => {\n const dispatch: AppDispatch = useAppDispatch()\n const [textInput, setTextInput] = useState('')\n\n const githubIssueList = useSelector((state: RootState) => state.githubIssue.issues)\n const loading = useSelector((state: RootState) => state.githubIssue.loading)\n const error = useSelector((state: RootState) => state.githubIssue.error)\n\n useEffect(() => {\n dispatch(fetchIssues())\n }, [dispatch])\n \n if (loading) {\n return <div>Loading...</div>;\n }\n\n if (error) {\n return <div>Error: {error}</div>;\n }\n\n const handleTextInputChange = (e:any) => {\n setTextInput(e.target.value)\n }\n\n const handleClick = () => {\n console.log(textInput)\n dispatch(addIssue(textInput))\n setTextInput('')\n }\n\n return(\n <div className=\"home_page\">\n <Box sx={{ml: '5rem', mr: '5rem'}}>\n <HelloWorld greeting='Hello from React Typescript' />\n <Typography variant=\"h4\" sx={{textAlign: 'center'}}>\n Project Issue Tracker\n </Typography>\n <Box sx={{\n display: 'flex',\n justifyContent: 'center'\n }}>\n <Stack spacing={2}>\n <Typography variant=\"h5\">\n Add new issue\n </Typography>\n <TextField \n id=\"outlined-basic\" \n label=\"Title\" \n variant=\"outlined\" \n onChange={handleTextInputChange}\n value={textInput}\n />\n <Button variant=\"contained\" onClick={handleClick}>Submit</Button>\n </Stack>\n </Box>\n <Box sx={{ml: '1rem', mt: '3rem'}}>\n <Typography variant=\"h5\" >\n Opened issue\n </Typography>\n {\n githubIssueList?.map((issue : string) => {\n return(\n <ProjectCard key={issue} issueTitle={issue} />\n )\n })\n }\n </Box>\n </Box>\n </div>\n )\n}\n\nexport default App\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"Redux Toolkit AsyncThunk Typescript",src:t(399587).A+"",width:"1372",height:"791"})})]})}function d(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(p,{...e})}):p(e)}},376442(e,n,t){const s=t.p+"assets/images/photo-kt443t6d_64hdh43hfh6dgjdfhg4_d-c3aedc7eed015cc5372c21f9c394693d.jpg";t.d(n,["A",0,s])},399587(e,n,t){const s=t.p+"assets/images/react-typescript_01-bf7334955c2a0a7dc4b51b23da4b3b8b.png";t.d(n,["A",0,s])},28453(e,n,t){t.d(n,{R:()=>o,x:()=>a});var s=t(296540);const r={},i=s.createContext(r);function o(e){const n=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.