如何使用 React 和 Rest API 构建网站(React 基础知识解释)
来源:dev.to
时间:2024-11-16 18:01:04 364浏览 收藏
大家好,今天本人给大家带来文章《如何使用 React 和 Rest API 构建网站(React 基础知识解释)》,文中内容主要涉及到,如果你对文章方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!
react 和 typescript 是用于构建可扩展、可维护和安全网站的强大框架。 react 提供了灵活且基于组件的架构,而 typescript 在 javascript 中添加了静态类型,以实现干净且可读的代码。本文将指导您使用 react 和 typescript 设置一个简单的网站,涵盖入门所需的核心概念。
为什么选择 react 和 typescript?
typescript 在 javascript 开发人员中很受欢迎,因为它可以在开发过程中捕获错误并使代码更易于理解和重构。两者非常适合构建现代、快速的网站和应用程序,并具有可扩展的可维护代码。
** 在 github 上查看完整代码:https://github.com/alexiacismaru/techtopia/tree/main/frontend
基本的 react 概念以及如何使用它们来构建网站
让我们为一个名为 techtopia 的虚构游乐园建立一个网站。我们将显示景点等元素以及它们在地图上的位置、登陆页面或加载页面。此外,我们还将允许添加/删除页面元素或基于变量搜索它们。
设置
通过将其复制到终端来创建一个空的 react 项目。
npm create vite@latest reactproject --template react-ts
然后运行空项目,浏览器窗口中将打开一个新选项卡。
cd reactproject npm run dev
最终项目结构概述
reactproject/ ├── node_modules/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── context/ │ ├── hooks/ │ ├── model/ │ ├── services/ │ ├── app.css │ ├── app.tsx │ ├── index.css │ ├── vite-env.d.ts ├── .gitignore ├── package.json └── tsconfig.json
成分
组件是也可以重用的网页元素。它们可以是网页的一部分,例如页眉或页脚,也可以是整个页面,例如用户列表。它就像一个 javascript 函数,但返回一个渲染的元素。
export function header() { return ( <header style={{ display: 'block', width: '100%', top: 0, left: 0, zindex: 'var(--header-and-footer)' }}> <div style={{ borderbottom: '1px solid white', boxshadow: '', backgroundcolor: 'transparent', paddingleft: '1rem', paddingright: '1rem', marginleft: 'auto', marginright: 'auto', }}> <div style={{ display: 'flex', justifycontent: 'space-between', padding: '5px', alignitems: 'baseline', }} > <a href='/techtopia' style={{ fontsize: '40px', fontfamily: 'marolla__', color: 'black', fontweight: 'bold', }}>techtopia</a> <div style={{display: 'flex', justifycontent: 'space-around', padding: '5px', alignitems: 'baseline',}}> <a href='/refreshment-stands' style={{ marginright: '10px', color: 'black' }}>refreshment stands</a> <a href='/attractions' style={{ marginright: '10px', color: 'white' }}>attractions</a> <a href='/map' style={{ marginright: '60px', color: 'white' }}>map</a> </div> </div> </div> </header> ) }
jsx
jsx 是 javascript xml,允许用户在 .jsx 文件中编写类似 html 的代码。
<button sx={{padding: "10px", color: 'black'}} onclick={onclose}>x</button>
多伦多证券交易所
tsx 是包含 jsx 语法的 typescript 文件的文件扩展名。借助 tsx,您可以使用现有的 jsx 语法编写经过类型检查的代码。
interface refreshmentstand { id: string; name: string; isopen: boolean; } const reshfresment = (props: refreshmentstand) => { return ( <div> <h1>{props.name}</h1> <p>{props.isopen}</p> </div> ); };
碎片
片段将多个元素返回给组件。它对元素列表进行分组,而不创建额外的 dom 节点。
我们可以使用它们从 java 后端获取数据(通过本文查看如何构建 java 应用程序:https://medium.com/@alexia.csmr/using-bounded-contexts-to-build -a-java-application-1c7995038d30)。首先安装 axios 并使用应用程序中的基本后端 url。然后,我们将创建一个使用 get 获取所有景点的片段。
import axios from 'axios' import { poi } from '../model/poi' const backend_url = 'http://localhost:8093/api' export const getattractions = async () => { const url = backend_url + '/attractions' const response = await axios.get<poi[]>(url) return response.data }
可以扩展到根据参数获取数据,post,delete等
export const addattraction = async (attractiondata: omit<poi, 'id'>) => { const url = backend_url + '/addattraction' const response = await axios.post(url, attractiondata) return response.data } export const getattraction = async (attractionid: string) => { const url = backend_url + '/attractions' const response = await axios.get<poi>(`${url}/${attractionid}`) return response.data } export const getattractionbytags = async (tags: string) => { const url = backend_url + '/attractions' const response = await axios.get<poi[]>(`${url}/tags/${tags}`) return response.data }
状态
状态是一个 react 对象,包含有关组件的数据或信息。组件的状态可能会随着时间的推移而发生变化,当发生变化时,组件会重新渲染。
要根据参数从列表中获取单个元素,您可以使用 useparams() 挂钩。
const { id } = useparams() const { isloading, iserror, attraction } = useattraction(id!) const { tag } = useparams() const { isloadingtag, iserrortag, attractions } = usetagsattractions(tag!)
挂钩
如上所示,我使用了_useattractions()和_usetagsattractions()。它们是钩子,可以进行个性化以获得您想要的任何数据。在此示例中,他们根据 id _或 _tags 获取景点。 hooks 只能在 react 函数组件内部调用,只能在组件的顶层调用,并且不能是有条件的。
import {usemutation, usequery, usequeryclient} from '@tanstack/react-query' import {poi} from "../model/./poi.ts"; import { addattraction, getattractions } from '../services/api.ts' import { usecontext } from 'react' export function useattractions() { const queryclient = usequeryclient() const { isloading: isdoingget, iserror: iserrorget, data: attractions, } = usequery({ querykey: ['attractions'], queryfn: () => getattractions(), }) const { mutate, isloading: isdoingpost, iserror: iserrorpost, } = usemutation((item: omit<poi, 'id'>) => addattraction(item), { onsuccess: () => { queryclient.invalidatequeries(['attractions']) }, }); return { isloading: isdoingget || isdoingpost, iserror: iserrorget || iserrorpost, attractions: attractions || [], addattraction: mutate } }
isloading 和 iserror
为了获得更好的 ui 体验,最好让用户知道发生了什么,即元素正在加载,或者执行此操作时出现错误。它们首先在钩子中声明,然后在组件中引入。
const navigate = usenavigate() const { isloading, iserror, attractions, addattraction } = useattractions() if (isloading) { return <loader /> } if (iserror) { return <alert severity='error'>error</alert> }
您还可以为更加自定义的网站创建单独的加载器或警报组件。
export default function loader() { return ( <div> <img alt="loading..." src="https://media0.giphy.com/media/rlqidjhbel1spmdlhz/giphy.gif?cid=6c09b9522vr2magrjgn620u5mfz1ymnqhpvg558dv13sd0g8&ep=v1_stickers_related&rid=giphy.gif&ct=s"/> <h3>loading...</h3> </div> ) }
现在,当页面加载时,用户将在屏幕上看到特殊的动画。
映射项目(列表和键)
如果你想显示列表中的所有元素,那么你需要映射所有元素。
import { usestate } from 'react' import { usenavigate } from 'react-router-dom' import { useattractions } from '../hooks/usepoi.ts' import { poi } from '../model/./poi.ts' export default function attractions() { const navigate = usenavigate() const { isloading, iserror, attractions, addattraction } = useattractions() return ( <div style={{ margintop: '70px' }}> {filteredattractions .map(({ id, name, image }: poi) => ( <div onclick={() => navigate(`/attractions/${id}`)} > <div> <img src={image} alt={name}/> <h3>{name}</h3> </div> </div> ))} </div> ) }
创建一个单独的文件,在其中声明 attraction 元素及其变量。
// ../model/poi.ts export interface poi { id: string; name: string; description: string; tags: string; agegroup: string; image: string; }
您可以在此处创建一个类型,以便稍后使用表单添加更多景点:
export type createpoi = omit<poi, 'id'>; # id is automatically generated so we don't need to manually add it
添加项目
我们已经创建了所需的片段和挂钩,因此现在我们可以制作一个表单,用户可以在其中写入属性并向网页添加新的吸引力。该表单是使用 mui 框架创建的。首先我将展示整个代码并分段解释。
import {createpoi} from "../model/./poi.ts"; import {z} from 'zod'; import {zodresolver} from "@hookform/resolvers/zod"; import {controller, useform} from "react-hook-form"; import { box, button, dialog, dialogactions, dialogcontent, dialogtitle, textfield, } from '@mui/material' interface attractiondialogprops { isopen: boolean; onsubmit: (attraction: createpoi) => void; onclose: () => void; } const itemschema: z.zodtype<createpoi> = z.object({ name: z.string().min(2, 'name must be at least 2 characters'), description: z.string(), tags: z.string(), agegroup: z.string(), image: z.string().url(), }) export function addattractiondialog({isopen, onsubmit, onclose}: attractiondialogprops) { const { handlesubmit, control, formstate: {errors}, } = useform<createpoi>({ resolver: zodresolver(itemschema), defaultvalues: { name: '', description: '', tags: '', agegroup: '', image: '', }, }); return ( <dialog open={isopen} onclose={onclose}> <form onsubmit={handlesubmit((data) => { onsubmit(data) onclose() })} > <div> <dialogtitle>add attraction</dialogtitle> <button onclick={onclose}> x </button> </div> <dialogcontent> <box> <controller name="name" control={control} render={({field}) => ( <textfield {...field} label="name" error={!!errors.name} helpertext={errors.name?.message} required /> )} /> <controller name="description" control={control} render={({field}) => ( <textfield {...field} label="description" error={!!errors.description} helpertext={errors.description?.message} /> )} /> <controller name="tags" control={control} render={({field}) => ( <textfield {...field} label="tags" error={!!errors.tags} helpertext={errors.tags?.message} required /> )} /> <controller name="agegroup" control={control} render={({field}) => ( <textfield {...field} label="age group" error={!!errors.agegroup} helpertext={errors.agegroup?.message} required /> )} /> <controller name="image" control={control} render={({field}) => ( <textfield {...field} label="image" error={!!errors.image} helpertext={errors.image?.message} required /> )} /> </box> </dialogcontent> <dialogactions> <button type="submit" variant="contained"> add </button> </dialogactions> </form> </dialog> ) }
如果您想让表单成为弹出窗口而不是单独的页面,请添加 isopen() 和 isclosed() 属性。 onsubmit() 是强制性的,因为这将触发 createpoi() 函数并将新对象添加到列表中。
interface attractiondialogprops { isopen: boolean; onsubmit: (attraction: createpoi) => void; onclose: () => void; }
为了进行用户表单验证,我们将安装并导入 zod。在这里声明输入需要什么格式以及是否有最小或最大长度等要求。
const itemschema: z.zodtype<createpoi> = z.object({ name: z.string().min(2, 'name must be at least 2 characters'), description: z.string(), tags: z.string(), agegroup: z.string(), image: z.string().url(), })
在组件内部,我们需要实现提交和用户验证。
const { handlesubmit, control, formstate: {errors}, } = useform<createpoi>({ resolver: zodresolver(itemschema), defaultvalues: { name: '', description: '', tags: '', agegroup: '', image: '', }, });
错误将在表单的 textfield 中与任何其他属性一起实现。
<textfield {...field} label="name" error={!!errors.name} helpertext={errors.name?.message} required />
确保表单可以在开始时关闭并提交。
<dialog open={isopen} onclose={onclose}> <form onsubmit={handlesubmit((data) => { onsubmit(data) onclose() })} > </form> </dialog>
您可以在另一个组件中实现此弹出窗口。
import { fab } from '@mui/material' import addicon from '@mui/icons-material/add' <fab size='large' aria-label='add' onclick={() => setisdialogopen(true)} > <addicon /> </fab>
删除项目
创建一个使用 delete 的钩子并在组件中实现它。
import { usemutation, usequery, usequeryclient } from '@tanstack/react-query' import { deleterefreshmentstand, getrefreshmentstand } from '../services/api.ts' import { usecontext } from 'react' export function userefreshmentstanditem(refreshmentstandid: string) { const queryclient = usequeryclient() const { isloading: isdoingget, iserror: iserrorget, data: refreshmentstand, } = usequery({ querykey: ['refreshmentstand'], queryfn: () => getrefreshmentstand(refreshmentstandid), }) const deleterefreshmentstandmutation = usemutation(() => deleterefreshmentstand(refreshmentstandid), { onsuccess: () => { queryclient.invalidatequeries(['refreshmentstands']); }, }); const handledeleterefreshmentstand = () => { deleterefreshmentstandmutation.mutate(); // trigger the delete mutation }; return { isloading: isdoingget || deleterefreshmentstandmutation.isloading, iserror: iserrorget || deleterefreshmentstandmutation.iserror, refreshmentstand, deleterefreshmentstand: handledeleterefreshmentstand, }; }
export default function refreshmentstand() { const { id } = useparams() const { isloading, iserror, refreshmentstand, deleterefreshmentstand } = userefreshmentstanditem(id!) if (isloading) { return <loader /> } if (iserror || !refreshmentstand) { return <alert severity='error'>error</alert> } return ( <> <cardmedia component='img' image={background} alt='background' /> <authheader /> <div style={{ display: 'flex', alignitems: 'center' }}> <div> <h1>{refreshmentstand.name}</h1> <p>status: {refreshmentstand.isopen ? 'open' : 'closed'}</p> /* implement the delete button */ <fab> <deleteicon onclick={deleterefreshmentstand}/> </fab> </div> <img src={refreshmentstand.image} alt='refreshmentstand image' /> </div> <footer /> </> ) }
过滤项目
在组件内部创建一个用于过滤文本输入的切换开关和一个根据年龄组或标签过滤景点的常量。可选链接 (?) 确保它处理 null 或未定义的值而不会出现错误。
const togglefilter = () => { setisfilteropen(!isfilteropen) } const filteredattractions = attractions .filter((attraction: poi) => attraction.agegroup?.tolowercase().includes(agegroupfilter.tolowercase()), ) .filter((attraction: poi) => attraction.tags?.tolowercase().includes(tagsfilter.tolowercase()), )
在迭代项目列表时包含它。
{filteredAttractions .filter((attraction: POI) => searchText.trim() === '' || attraction.name.toLowerCase().includes(searchText.toLowerCase()), ) .map(({ id, name, image }: POI) => ( <div> <div> <img src={image} alt={name} /> <h3>{name}</h3> </div> </div> ))}
结论
将 react 与 typescript 结合使用使您能够构建易于维护和扩展的动态、安全的网站。 typescript 的类型检查可以防止运行时错误,而 react 基于组件的结构可以有效地组织项目。
文中关于的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《如何使用 React 和 Rest API 构建网站(React 基础知识解释)》文章吧,也可关注golang学习网公众号了解相关技术文章。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
105 收藏
-
273 收藏
-
160 收藏
-
297 收藏
-
381 收藏
-
439 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习