<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React、TypeScript、NodeJS 和 MongoDB 搭建 Todo App

          共 13270字,需瀏覽 27分鐘

           ·

          2020-08-27 12:29

          在本教程中,我們將在服務(wù)器和客戶端使用 TypeScript、React、NodeJS、Express 和 MongoDB 從頭開始構(gòu)建一個(gè) Todo 應(yīng)用程序。

          我們從設(shè)計(jì) API 開始。

          • 用 NodeJS, Express, MongoDB 和 TypeScript 設(shè)計(jì) API
          • 啟動(dòng)
          • 創(chuàng)建 Todo 類型
          • 創(chuàng)建 Todo 模塊
          • 創(chuàng)建 API 控制器
          • 獲取、新增、更新和刪除 Todo
          • 創(chuàng)建 API 路由
          • 創(chuàng)建服務(wù)器
          • 用 React 和 TypeScript 創(chuàng)建客戶端
          • 啟動(dòng)
          • 創(chuàng)建 Todo 類型
          • 從 API 獲取數(shù)據(jù)
          • 創(chuàng)建組件
          • 添加 Todo 表單
          • 展示 Todo
          • 獲取和展示數(shù)據(jù)
          • 資源

          用 NodeJS, Express, MongoDB 和 TypeScript 設(shè)計(jì) API

          啟動(dòng)

          如果你是新手,可以看看《TypeScript 實(shí)用指南》,或者從《如何用 Node JS、Express 和 MongoDB 從頭創(chuàng)建 API》。如果你有一定經(jīng)驗(yàn)了,可以直接開始。

          在終端上運(yùn)行這個(gè)命令,創(chuàng)建一個(gè)新的 NodeJS 應(yīng)用程序:

          ?yarn?init

          它會(huì)詢問幾個(gè)問題,然后初始化應(yīng)用程序。你可以通過向命令中添加 ?-y??標(biāo)志來(lái)跳過。

          然后,按照以下目錄構(gòu)建項(xiàng)目:

          ├──?dist
          ├──?node_modules
          ├──?src
          ???├──?app.ts
          ???├──?controllers
          ???|??└──?todos
          ???|?????└──?index.ts
          ???├──?models
          ???|??└──?todo.ts
          ???├──?routes
          ???|??└──?index.ts
          ???└──?types
          ??????└──?todo.ts
          ├──?nodemon.json
          ├──?package.json
          ├──?tsconfig.json

          如你所見,這個(gè)文件結(jié)構(gòu)相對(duì)簡(jiǎn)單。代碼編譯成純 JavaScript 后,dist 目錄將用作輸出文件夾。

          我們還有一個(gè) ?app.ts,它是服務(wù)器的入口??刂破鳌㈩愋秃吐酚梢苍谒鼈兏髯砸运鼈兠牡奈募A中。

          現(xiàn)在,我們需要配置 ?tsconfig.json,使編譯器運(yùn)行我們的首選項(xiàng)。

          • tsconfig.json
          {
          ??"compilerOptions":?{
          ????"target":?"es6",
          ????"module":?"commonjs",
          ????"outDir":?"dist/js",
          ????"rootDir":?"src",
          ????"strict":?true,
          ????"esModuleInterop":?true,
          ????"forceConsistentCasingInFileNames":?true
          ??},
          ??"include":?["src/**/*"],
          ??"exclude":?["src/types/*.ts",?"node_modules",?".vscode"]
          }

          這里強(qiáng)調(diào)四個(gè)主要屬性:

          outDir: 告訴編譯器把編譯好的代碼放進(jìn) ?dist/js??文件夾

          rootDir: 告訴 TypeScript 編譯 src 文件夾中的每個(gè) .ts 文件

          include: 告訴編譯器包含 src 目錄和子目錄中的文件

          exclude: 在編譯時(shí)會(huì)排除數(shù)組中的文件或文件夾

          現(xiàn)在我們安裝依賴項(xiàng),使項(xiàng)目可以使用 TypeScript。因?yàn)槟J(rèn)情況下,這個(gè)應(yīng)用程序會(huì)使用 JavaScript。

          在 NodeJS 應(yīng)用程序中有兩種使用 TypeScript 的方法,要么在項(xiàng)目中本地安裝使用,要么在電腦中全局安裝使用?;趥€(gè)人喜好,我會(huì)選擇后者。但如果你想,你也可以堅(jiān)持使用本地安裝使用的方式。

          現(xiàn)在,讓我們?cè)诮K端上執(zhí)行以下命令來(lái)安裝 TypeScript。

          ?yarn?add?typescript?-g

          這個(gè) ?g??標(biāo)志允許全局安裝 TypeScript,這樣它就能在計(jì)算機(jī)任何地方使用。

          接下來(lái),為了使用 Express 和 MongoDB,我們安裝一些依賴項(xiàng)。

          yarn?add?express?cors?mongoose

          我們還需要安裝它們的類型作為開發(fā)依賴項(xiàng),幫助 TypeScript 編譯器理解這些包。

          yarn?add?-D?@types/node?@types/express?@types/mongoose?@types/cors

          現(xiàn)在,TypeScript 不會(huì)再對(duì)你提示錯(cuò)誤——它將使用這些類型來(lái)定義我們剛剛安裝的庫(kù)。

          我們還需要安裝其他依賴項(xiàng),以便能夠編譯 TypeScript 代碼并同時(shí)啟動(dòng)服務(wù)器。

          yarn?add?-D?concurrently?nodemon

          有了這些,我們現(xiàn)在就可以更新 ?package.json??的 scripts 來(lái)啟動(dòng)服務(wù)器。

          • package.json
          "scripts":?{
          ????"build":?"tsc",
          ????"start":?"concurrently?\"tsc?-w\"?\"nodemon?dist/js/app.js\""
          ??}
          concurrently??幫助編譯 TypeScript 代碼,持續(xù)觀察變化,同時(shí)啟動(dòng)服務(wù)器。也就是說(shuō),我們現(xiàn)在可以啟動(dòng)服務(wù)器了——但是,我們還沒有創(chuàng)建一些有意義的東西。所以,讓我們?cè)谙乱还?jié)中解決這個(gè)問題。

          創(chuàng)建 Todo 類型

          • types/todo.ts
          import?{?Document?}?from?"mongoose"

          export?interface?ITodo?extends?Document?{
          ??name:?string
          ??description:?string
          ??status:?boolean
          }

          這里,我們有了繼承 ?mongoose??提供的 ?Document??類型的 Todo 接口。稍后我們將使用它與 MongoDB 交互。也就是說(shuō),我們現(xiàn)在可以定義 Todo 模塊。

          創(chuàng)建 Todo 模塊

          • models/todo.ts

          import?{?ITodo?}?from?"./../types/todo"
          import?{?model,?Schema?}?from?"mongoose"

          const?todoSchema:?Schema?=?new?Schema(
          ??{
          ????name:?{
          ??????type:?String,
          ??????required:?true,
          ????},

          ????description:?{
          ??????type:?String,
          ??????required:?true,
          ????},

          ????status:?{
          ??????type:?Boolean,
          ??????required:?true,
          ????},
          ??},
          ??{?timestamps:?true?}
          )

          export?default?model("Todo",?todoSchema)

          首先導(dǎo)入 ?ITodo??接口和 一些 ?mongoose??導(dǎo)出的模塊,后者是幫助定義 Todo schema 和在導(dǎo)出前把 ITodo 作為類型參數(shù)傳入 ?model??。

          這樣,我們現(xiàn)在就可以在其他文件中使用 Todo 模塊來(lái)與數(shù)據(jù)庫(kù)交互。

          創(chuàng)建 API 控制器

          獲取、新增、更新和刪除 Todos

          • controllers/todos/index.ts

          import?{?Response,?Request?}?from?"express"
          import?{?ITodo?}?from?"./../../types/todo"
          import?Todo?from?"../../models/todo"

          const?getTodos?=?async?(req:?Request,?res:?Response):?Promise<void>?=>?{
          ??try?{
          ????const?todos:?ITodo[]?=?await?Todo.find()
          ????res.status(200).json({?todos?})
          ??}?catch?(error)?{
          ????throw?error
          ??}
          }

          這里,我們首先需要從 ?express??導(dǎo)入一些類型,因?yàn)槲蚁腼@式指明類型。如果你想,你可以讓 TypeScript 幫你推斷。

          接下來(lái),我們使用 getTodos() 函數(shù)來(lái)獲取數(shù)據(jù),它接收 ?req??和 ?res??參數(shù)并返回 promise。

          在前面創(chuàng)建的 Todo 模塊的幫助下,我們現(xiàn)在可以從 MongoDB 獲取數(shù)據(jù)并返回 Todo 數(shù)組。

          • controllers/todos/index.ts


          const?addTodo?=?async?(req:?Request,?res:?Response):?Promise<void>?=>?{
          ??try?{
          ????const?body?=?req.body?as?Pick"name"?|?"description"?|?"status">

          ????const?todo:?ITodo?=?new?Todo({
          ??????name:?body.name,
          ??????description:?body.description,
          ??????status:?body.status,
          ????})

          ????const?newTodo:?ITodo?=?await?todo.save()
          ????const?allTodos:?ITodo[]?=?await?Todo.find()

          ????res
          ??????.status(201)
          ??????.json({?message:?"Todo?added",?todo:?newTodo,?todos:?allTodos?})
          ??}?catch?(error)?{
          ????throw?error
          ??}
          }

          addTodo()??函數(shù)接收包含用戶輸入數(shù)據(jù)的 body 對(duì)象。

          接下來(lái),我使用類型轉(zhuǎn)換來(lái)避免拼寫錯(cuò)誤,并限制 ?body??變量與 ?ITodo??類型匹配,然后基于該模塊創(chuàng)建一個(gè)新的 Todo。

          有了這些,我們現(xiàn)在可以在 DB 中保存 Todo 并返回新增的 Todo 和更新后的 todos 數(shù)組。

          • controllers/todos/index.ts

          const?updateTodo?=?async?(req:?Request,?res:?Response):?Promise<void>?=>?{
          ??try?{
          ????const?{
          ??????params:?{?id?},
          ??????body,
          ????}?=?req
          ????const?updateTodo:?ITodo?|?null?=?await?Todo.findByIdAndUpdate(
          ??????{?_id:?id?},
          ??????body
          ????)
          ????const?allTodos:?ITodo[]?=?await?Todo.find()
          ????res.status(200).json({
          ??????message:?"Todo?updated",
          ??????todo:?updateTodo,
          ??????todos:?allTodos,
          ????})
          ??}?catch?(error)?{
          ????throw?error
          ??}
          }
          為了實(shí)現(xiàn)更新 todo, 我們需要拿到 id 和從 ?req??對(duì)象中獲取 body,然后把他們傳入 ?findByIdAndUpdate(),這個(gè)函數(shù)將會(huì)在數(shù)據(jù)庫(kù)中找到 Todo 并且更新它。
          • controllers/todos/index.ts

          const?deleteTodo?=?async?(req:?Request,?res:?Response):?Promise<void>?=>?{
          ??try?{
          ????const?deletedTodo:?ITodo?|?null?=?await?Todo.findByIdAndRemove(
          ??????req.params.id
          ????)
          ????const?allTodos:?ITodo[]?=?await?Todo.find()
          ????res.status(200).json({
          ??????message:?"Todo?deleted",
          ??????todo:?deletedTodo,
          ??????todos:?allTodos,
          ????})
          ??}?catch?(error)?{
          ????throw?error
          ??}
          }

          export?{?getTodos,?addTodo,?updateTodo,?deleteTodo?}

          deleteTodo()??函數(shù)允許你從數(shù)據(jù)庫(kù)中刪除 Todo。在這里,我們從 req 中拿到 id,并把它作為參數(shù)傳遞給 ?findByIdAndRemove(),來(lái)獲取到對(duì)應(yīng)的 Todo 并從 DB 中刪除它。

          接下來(lái),導(dǎo)出這些函數(shù)以便我們?cè)谄渌募惺褂盟鼈儭R簿褪钦f(shuō),我們現(xiàn)在可以為 API 創(chuàng)建一些路由,并使用這些方法來(lái)處理請(qǐng)求。

          創(chuàng)建 API 路由

          • routes/index.ts
          import?{?Router?}?from?"express"
          import?{?getTodos,?addTodo,?updateTodo,?deleteTodo?}?from?"../controllers/todos"

          const?router:?Router?=?Router()

          router.get("/todos",?getTodos)

          router.post("/add-todo",?addTodo)

          router.put("/edit-todo/:id",?updateTodo)

          router.delete("/delete-todo/:id",?deleteTodo)

          export?default?router
          我們創(chuàng)建四個(gè)路由對(duì)應(yīng)從數(shù)據(jù)庫(kù)中獲取、新增、更新和刪除 todo。因?yàn)槲覀円呀?jīng)創(chuàng)建了函數(shù),所以唯一要做的就是導(dǎo)入這些方法并將它們作為參數(shù)傳遞。

          到目前為止,我們已經(jīng)談了很多,但是仍然沒有啟動(dòng)服務(wù)器。所以,我們?cè)谙乱还?jié)中解決這個(gè)問題。

          創(chuàng)建服務(wù)器

          在創(chuàng)建服務(wù)器之前,我們需要在 ?nodemon.json??加一些環(huán)境變量來(lái)保存 MongoDB 的憑據(jù)。

          • nodemon.json

          {
          ????"env":?{
          ????????"MONGO_USER":?"your-username",
          ????????"MONGO_PASSWORD":?"your-password",
          ????????"MONGO_DB":?"your-db-name"
          ????}
          }
          你可以在 ?MongoDB Atlas,通過創(chuàng)一個(gè)新集群來(lái)得到憑據(jù)。
          • app.ts

          import?express,?{?Express?}?from?"express"
          import?mongoose?from?"mongoose"
          import?cors?from?"cors"
          import?todoRoutes?from?"./routes"

          const?app:?Express?=?express()

          const?PORT:?string?|?number?=?process.env.PORT?||?4000

          app.use(cors())
          app.use(todoRoutes)

          const?uri:?string?=?`mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@clustertodo.raz9g.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`
          const?options?=?{?useNewUrlParser:?true,?useUnifiedTopology:?true?}
          mongoose.set("useFindAndModify",?false)

          mongoose
          ??.connect(uri,?options)
          ??.then(()?=>
          ????app.listen(PORT,?()?=>
          ??????console.log(`Server?running?on?http://localhost:${PORT}`)
          ????)
          ??)
          ??.catch(error?=>?{
          ????throw?error
          ??})
          這里,我們首先從導(dǎo)入 ?express??庫(kù)開始,這使用我們能調(diào)用 ?use()??方法,這個(gè)方法將幫助處理 Todo 路由。

          然后,我們用 ?mongoose??包,通過讀取 ?nodemon.json??帶憑證的 url 去連接 MongoDB。

          就是說(shuō),現(xiàn)在如果我們能成功連接 MongoDB,服務(wù)器就會(huì)啟動(dòng),否則,會(huì)拋出錯(cuò)誤。

          我們現(xiàn)在已經(jīng)通過 Node、Express、TypeScript 和 MongoDB 完成 api 的構(gòu)建。現(xiàn)在我們開始用 React 和 TypeScript 構(gòu)建客戶端。

          用 React 和 TypeScript 創(chuàng)建客戶端

          構(gòu)建

          為了創(chuàng)建一個(gè)新的 React 應(yīng)用,我將會(huì)使用 create-react-app ——你可以用其他你想用的方法。

          所以,在終端運(yùn)行以下代碼:

          npx?create-react-app?my-app?--template?typescript

          然后,為了能獲取遠(yuǎn)程數(shù)據(jù)安裝 Axios 庫(kù)。

          ??yarn?add?axios

          安裝完成后,按照以下目錄構(gòu)建項(xiàng)目:

          ├──?node_modules
          ├──?public
          ├──?src
          |??├──?API.ts
          |??├──?App.test.tsx
          |??├──?App.tsx
          |??├──?components
          |??|??├──?AddTodo.tsx
          |??|??└──?TodoItem.tsx
          |??├──?index.css
          |??├──?index.tsx
          |??├──?react-app-env.d.ts
          |??├──?setupTests.ts
          |??└──?type.d.ts
          ├──?tsconfig.json
          ├──?package.json
          └──?yarn.lock

          這樣,我們有一個(gè)相對(duì)簡(jiǎn)單的文件結(jié)構(gòu)。最值得注意的是 ?src/type.d.ts??被用來(lái)存放類型。我?guī)缀踉诿總€(gè)文件中都使用了它們,所以我添加了擴(kuò)展 ?.d.ts??,使類型全局可用?,F(xiàn)在我們不再需要導(dǎo)入它們。

          創(chuàng)建 Todo 類型

          • src/type.d.ts

          interface?ITodo?{
          ??_id:?string
          ??name:?string
          ??description:?string
          ??status:?boolean
          ??createdAt?:?string
          ??updatedAt?:?string
          }

          interface?TodoProps?{
          ??todo:?ITodo
          }

          type?ApiDataType?=?{
          ??message:?string
          ??status:?string
          ??todos:?ITodo[]
          ??todo?:?ITodo
          }

          這里, ?ITodo??接口需要跟 API 返回的數(shù)據(jù)類型一樣。這里沒有 ?mongoose? , 所以需要加一些額外的屬性來(lái)匹配 API 定義的數(shù)據(jù)類型。

          然后,我們用相同的的接口定義 ?TodoProps??,組件會(huì)接受它并渲染數(shù)據(jù)。

          現(xiàn)在我們已經(jīng)定義了類型——現(xiàn)在讓我們開始從 API 獲取數(shù)據(jù)。

          從API獲取數(shù)據(jù)

          • src/API.ts

          import?axios,?{?AxiosResponse?}?from?"axios"

          const?baseUrl:?string?=?"http://localhost:4000"

          export?const?getTodos?=?async?():?Promise>?=>?{
          ??try?{
          ????const?todos:?AxiosResponse?=?await?axios.get(
          ??????baseUrl?+?"/todos"
          ????)
          ????return?todos
          ??}?catch?(error)?{
          ????throw?new?Error(error)
          ??}
          }
          我們需要導(dǎo)入 ?axios,通過 api 來(lái)請(qǐng)求數(shù)據(jù),然后,用 ?getTodos()??函數(shù)從服務(wù)端獲取數(shù)據(jù)。它將返回 ?AxiosResponse??為類型的 promise, 保存獲取到的 ?ApiDataType??類型的 Todos。
          • src/API.ts

          export?const?addTodo?=?async?(
          ??formData:?ITodo
          ):?Promise>?=>?{
          ??try?{
          ????const?todo:?Omit"_id">?=?{
          ??????name:?formData.name,
          ??????description:?formData.description,
          ??????status:?false,
          ????}
          ????const?saveTodo:?AxiosResponse?=?await?axios.post(
          ??????baseUrl?+?"/add-todo",
          ??????todo
          ????)
          ????return?saveTodo
          ??}?catch?(error)?{
          ????throw?new?Error(error)
          ??}
          }

          這個(gè)函數(shù)接受用戶輸入的數(shù)據(jù)作為參數(shù)并返回 promise。這里,我們需要去掉 ?_id??屬性因?yàn)?MongoDB 會(huì)自動(dòng)生成。

          • src/API.ts
          export?const?updateTodo?=?async?(
          ??todo:?ITodo
          ):?Promise>?=>?{
          ??try?{
          ????const?todoUpdate:?Pick"status">?=?{
          ??????status:?true,
          ????}
          ????const?updatedTodo:?AxiosResponse?=?await?axios.put(
          ??????`${baseUrl}/edit-todo/${todo._id}`,
          ??????todoUpdate
          ????)
          ????return?updatedTodo
          ??}?catch?(error)?{
          ????throw?new?Error(error)
          ??}
          }
          為了實(shí)現(xiàn)更新 Todo,我們必須傳入更新后的數(shù)據(jù)和對(duì)象 id。這里,我們需要更改 Todo 的 ?狀態(tài)??,那么在發(fā)送到服務(wù)器之前我們只需要選擇所需的屬性即可。
          • src/API.ts

          export?const?deleteTodo?=?async?(
          ??_id:?string
          ):?Promise>?=>?{
          ??try?{
          ????const?deletedTodo:?AxiosResponse?=?await?axios.delete(
          ??????`${baseUrl}/delete-todo/${_id}`
          ????)
          ????return?deletedTodo
          ??}?catch?(error)?{
          ????throw?new?Error(error)
          ??}
          }
          這里,我們也有一個(gè)函數(shù)接受 ?_id??屬性作為參數(shù)并返回 promise。

          有了這些,我們現(xiàn)在可以轉(zhuǎn)到 components 文件夾并向其文件中添加一些有意義的代碼。

          創(chuàng)建組件

          增加 Todo 表單

          • components/AddTodo.tsx

          import?React?from?"react"

          type?Props?=?TodoProps?&?{
          ??updateTodo:?(todo:?ITodo)?=>?void
          ??deleteTodo:?(_id:?string)?=>?void
          }

          const?Todo:?React.FC?=?({?todo,?updateTodo,?deleteTodo?})?=>?{
          ??const?checkTodo:?string?=?todo.status???`line-through`?:?""
          ??return?(
          ????<div?className="Card">
          ??????<div?className="Card--text">
          ????????<h1?className={checkTodo}>{todo.name}h1>

          ????????<span?className={checkTodo}>{todo.description}span>
          ??????div>
          ??????<div?className="Card--button">
          ????????<button
          ??????????onClick={()?=>
          ?updateTodo(todo)}
          ??????????className={todo.status???`hide-button`?:?"Card--button__done"}
          ????????>
          ??????????Complete
          ????????button>
          ????????<button
          ??????????onClick={()?=>
          ?deleteTodo(todo._id)}
          ??????????className="Card--button__delete"
          ????????>
          ??????????Delete
          ????????button>
          ??????div>
          ????div>
          ??)
          }

          export?default?Todo
          這里有一個(gè) React 類型的函數(shù)組件。FC (FC 代表函數(shù)組件),它接收 ?saveTodo()??方法為 props,該方法允許我們將數(shù)據(jù)保存到數(shù)據(jù)庫(kù)。

          然后,我們創(chuàng)建 ?formData? state,它需要匹配 ITodo 類型來(lái)滿足編譯器的要求。這就是我們將它傳遞給 useState hook 的原因。我們還需要添加一個(gè)替代類型({}),因?yàn)槌跏紶顟B(tài)是個(gè)空對(duì)象。

          有了這些,我們現(xiàn)在可以繼續(xù)下一步,展示獲取的數(shù)據(jù)。

          展示 Todo

          • components/TodoItem.tsx

          import?React?from?"react"

          type?Props?=?TodoProps?&?{
          ??updateTodo:?(todo:?ITodo)?=>?void
          ??deleteTodo:?(_id:?string)?=>?void
          }

          const?Todo:?React.FC?=?({?todo,?updateTodo,?deleteTodo?})?=>?{
          ??const?checkTodo:?string?=?todo.status???`line-through`?:?""
          ??return?(
          ????<div?className="Card">
          ??????<div?className="Card--text">
          ????????<h1?className={checkTodo}>{todo.name}h1>

          ????????<span?className={checkTodo}>{todo.description}span>
          ??????div>
          ??????<div?className="Card--button">
          ????????<button
          ??????????onClick={()?=>
          ?updateTodo(todo)}
          ??????????className={todo.status???`hide-button`?:?"Card--button__done"}
          ????????>
          ??????????Complete
          ????????button>
          ????????<button
          ??????????onClick={()?=>
          ?deleteTodo(todo._id)}
          ??????????className="Card--button__delete"
          ????????>
          ??????????Delete
          ????????button>
          ??????div>
          ????div>
          ??)
          }

          export?default?Todo
          這里,我們需要繼承 ?TodoProps??類型并加入 ?updateTodo??和 ?deleteTodo??函數(shù),作為 props 傳遞給組件。

          現(xiàn)在,當(dāng)傳入 Todo 對(duì)象,我們將能夠顯示它并更新或刪除 Todo。

          太棒了!現(xiàn)在我們可以到 ?App.tsx??文件并把最后一塊拼圖放進(jìn)去。

          獲取和展示數(shù)據(jù)

          • App.tsx

          import?React,?{?useEffect,?useState?}?from?'react'
          import?TodoItem?from?'./components/TodoItem'
          import?AddTodo?from?'./components/AddTodo'
          import?{?getTodos,?addTodo,?updateTodo,?deleteTodo?}?from?'./API'

          const?App:?React.FC?=?()?=>?{
          ??const?[todos,?setTodos]?=?useState([])

          ??useEffect(()?=>?{
          ????fetchTodos()
          ??},?[])

          ??const?fetchTodos?=?():?void?=>?{
          ????getTodos()
          ????.then(({?data:?{?todos?}?}:?ITodo[]?|?any)?=>?setTodos(todos))
          ????.catch((err:?Error)?=>?console.log(err))
          ??}
          這里,我們首先導(dǎo)入組件和 ?API.ts??導(dǎo)出的函數(shù)。然后,我們傳遞 ?ITodo??類型的數(shù)組給 ?useState??并且把它初始化為空數(shù)組。

          getTodos()??方法會(huì)返回 promise —— 因此,我們可以調(diào)用 then 函數(shù)并用獲取到的數(shù)據(jù)更新 state,或者在發(fā)生任何錯(cuò)誤時(shí)拋出一個(gè)錯(cuò)誤。

          有了這些,我們現(xiàn)在可以在組件組件成功掛載之后,調(diào)用 ?fetchTodos()??函數(shù)。

          • App.tsx
          const?handleSaveTodo?=?(e:?React.FormEvent,?formData:?ITodo):?void?=>?{
          ??e.preventDefault()
          ??addTodo(formData)
          ????.then(({?status,?data?})?=>?{
          ??????if?(status?!==?201)?{
          ????????throw?new?Error("Error!?Todo?not?saved")
          ??????}
          ??????setTodos(data.todos)
          ????})
          ????.catch(err?=>?console.log(err))
          }
          當(dāng)發(fā)送表單時(shí),我們用 ?addTodo()??向服務(wù)端發(fā)送請(qǐng)求。如果 Todo 被成功保存,我們將更新數(shù)據(jù),否則將會(huì)拋出錯(cuò)誤。
          • App.tsx

          const?handleUpdateTodo?=?(todo:?ITodo):?void?=>?{
          ??updateTodo(todo)
          ????.then(({?status,?data?})?=>?{
          ??????if?(status?!==?200)?{
          ????????throw?new?Error("Error!?Todo?not?updated")
          ??????}
          ??????setTodos(data.todos)
          ????})
          ????.catch(err?=>?console.log(err))
          }

          const?handleDeleteTodo?=?(_id:?string):?void?=>?{
          ??deleteTodo(_id)
          ????.then(({?status,?data?})?=>?{
          ??????if?(status?!==?200)?{
          ????????throw?new?Error("Error!?Todo?not?deleted")
          ??????}
          ??????setTodos(data.todos)
          ????})
          ????.catch(err?=>?console.log(err))
          }
          更新和刪除 Todo 函數(shù)是很類似的。它們都接受參數(shù),發(fā)送請(qǐng)求并得到響應(yīng),然后它們會(huì)檢查請(qǐng)求是否成功并作相應(yīng)處理。
          • App.tsx

          return?(
          ????
          ??????

          My?Todos


          ??????
          ??????{todos.map((todo:?ITodo)?=>?(
          ??????????????????key={todo._id}
          ??????????updateTodo={handleUpdateTodo}
          ??????????deleteTodo={handleDeleteTodo}
          ??????????todo={todo}
          ????????/>
          ??????))}
          ????

          ??)
          }

          export?default?App
          這里我們遍歷 ?todos??數(shù)組并將所需的數(shù)據(jù)傳遞給 ?TodoItem。

          現(xiàn)在,如果你打開服務(wù)器端應(yīng)用程序的文件夾(并在終端中執(zhí)行以下命令):

          yarn?start

          在客戶端也如此:

          yarn?start

          你應(yīng)該能看到我們的 Todo 應(yīng)用程序會(huì)按預(yù)期工作。

          太棒了!最后,我們使用 TypeScript、React、NodeJs、Express 和 MongoDB 完成了一個(gè) Todo 應(yīng)用程序的構(gòu)建。

          附上源代碼。

          謝謝閱讀!



          原文鏈接:https://www.freecodecamp.org/news/how-to-build-a-todo-app-with-react-typescript-nodejs-and-mongodb/

          作者:Ibrahima Ndaw

          譯者:cyan.wu


          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          React + TypeScript 實(shí)踐
          全棧前端精選
          0
          React + TypeScript 實(shí)踐
          前端迷
          0
          React + TypeScript 實(shí)踐
          前端圍城
          0
          React 項(xiàng)目實(shí)踐——搭建一個(gè)溫度控制 App
          程序源代碼
          0
          Typescript結(jié)合React實(shí)踐
          前端宇宙
          0
          Typescript結(jié)合React實(shí)踐
          魚頭的Web海洋
          0
          使用 nodejs 和 ElasticSearch 快速搭建全文檢索
          有關(guān)SQL
          0
          使用 Typescript 開發(fā) Nodejs 命令行工具
          程序員成長(zhǎng)指北
          0
          使用 Typescript 開發(fā) Nodejs 命令行工具
          web前端開發(fā)
          0
          Vite + React + Typescript 構(gòu)建實(shí)戰(zhàn)
          前端Q
          0
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色色视频免费看 | 一本色道久久综合狠狠躁小说 | 又黄又爽一区二区三区 | 久久高凊无码免费一区 | 欧美亚洲中文日 |