1、前言
1、zustand
精悍的状态管理器
2、axios
强大的http请求库
3、query-string
query-string
是一个用于解析和字符串化 URL 查询字符串的库。在处理 URL 查询参数时,它提供了比原生 JavaScript API 更简单和强大的方法。
2、分阶段开发
1、Auth UI
用于生成注册组件以及相关的一系列子组件
- hooks:使用zustand技术达成状态管理的功能
- 通讯技术:使用axios技术达成
- react-hook-form : 用于处理表单功能
2、注册功能、MongoDB、Prisma 设置
- 安装prisma:npm i -D prisma
- 初始化:npx prisma init
- prisma配置文件:/prisma/schema.prisma
- 将“datasource db ”下的“provider”的值修改为mongodb
- 全局配置文件:/.env 记录了数据库的地址
- prisma配置文件:/prisma/schema.prisma
3、登录功能
- API Route […nextauth].ts:处理认证请求,与社交媒体和数据库交互。
- schema.prisma:定义数据库模型和关系,与数据库直接互动。
- getCurrentUser.ts:从 NextAuth.js 检索用户会话数据,使用会话中的电子邮件查询数据库以获取用户详情。
注意:
- 社交登录(google、github)是对数据库字段和结构有要求的
- getCurrentUser.ts中其实是调用了next的next-auth/next库来进行session的获取,背后实际是使用了jwt进行cookie的保存,但是所有逻辑对外不可见罢了
- nextauth.ts依赖于prisma的查询来实现登录逻辑
- getCurrentUser.ts依赖于nextauth.ts中的options来进行登录用户信息的获取
4、github登录功能
- 登录你的github 进入“setting”
- 进入“Developer setting”
- 选择“OAth apps”
- 填写信息,
- 创建一个秘钥
- 记住:
- 在.env中写入:
- GITHUB_ID=”Ov23limd89i6q7aN1BS2″
GITHUB_SECRET=”ebc396bd2b43fc1f717ecdac17b82fb2081e20ef”
- GITHUB_ID=”Ov23limd89i6q7aN1BS2″
- 进入:src/app/components/modals/RegisterModal.tsx
- import { signIn } from “next-auth/react”;
- onClick={()=>signIn(“github”)}
- 设置avatar为github头像时遇到问题
此时需要配置:next.config.mjs
5、google登录
- 登录https://console.cloud.google.com/welcome/new?pli=1
- 填入:http://localhost:3000/api/auth/callback/google
- ID:641536683817-eneg0ddasrt593gj43rhk58rsv524d4f.apps.googleusercontent.com
- 秘钥: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
- 之后查看信息:https://console.cloud.google.com/apis/credentials?project=custom-airbnb-423610
- 在.env中填写google信息
- 在src/app/components/modals/RegisterModal.tsx和src/app/components/modals/LoginModal.tsx 填写()=>signIn(“google”)
6、列表创建步骤 1(类别选择)

- react-hook-form的使用方法
- watch 用于监听defaultValues中的字段,当字段发生变动时会通知对应的变量,注意:这里变量(category)仍然是string类型,并没有被改造为一个响应式的object类型
- setValue 用于设置defaultValues中的值,当然你可以设置一些参数来对应更多的功能:
- shouldDirty:
- 作用: 如果设置为
true
,则会将字段标记为“脏的”。一个“脏的”字段意味着其值已经被用户修改过。 - 示例: 假设一个表单字段最初的值是
1
,然后你使用setValue
将其值改为2
。如果shouldDirty
设置为true
,该字段将被标记为“脏的”。 - 场景:提交按钮状态。某些表单设计中,只有在用户修改了字段后才启用提交按钮。设置为 true 可以使得提交按钮在合适的时候启用。
- 作用: 如果设置为
- shouldTouch:
- 作用: 如果设置为
true
,则会将字段标记为“已触碰”。一个“已触碰”的字段意味着用户已经与该字段进行过交互(例如,点击或聚焦)。 - 示例: 即使用户没有手动点击字段,只是通过
setValue
更改了字段的值,如果shouldTouch
设置为true
,该字段也会被标记为“已触碰”。 - 场景:表单聚焦。当用户开始填写表单时,可能希望自动聚焦到第一个未触碰过的字段上,以提高填写效率。
- 作用: 如果设置为
- shouldValidate:
- 作用: 如果设置为
true
,则会触发该字段的验证。即在设置新值之后立即对该字段进行验证。 - 示例: 如果字段有验证规则(例如,必填项或特定格式),当你通过
setValue
改变字段值并设置shouldValidate
为true
时,将立即对该字段进行验证,验证结果会影响errors
对象。 - 场景:错误信息更新。如果字段值不符合验证规则,会更新与该字段相关的错误信息,以便及时向用户显示错误提示。
- 作用: 如果设置为
- shouldDirty:
7、列表创建步骤 2(位置选择、地图组件、国家/地区自动完成)
- 国家和城市列表选择
- npm install world-countries
import { create } from "zustand";
import countries from "world-countries"
const formattedCountries = countries.map((country)=>({
value:country.cca2,
label:country.name.common,
flag:country.flag,
latlng:country.latlng,
region:country.region
}))
const useCountries=()=>{
const getAll=()=>formattedCountries;
const getByValue=(value:string)=>{
return formattedCountries.find((item )=>item.value === value);
}
return {
getAll,
getByValue
}
}
export default useCountries;
// const useCountries = create()
// export default useCountries;
- useCountries
- 可以看出,world-countries是提供了一个全球国家的列表
- flag:国旗字符标识
cca2
是world-countries
数据中每个国家的 Alpha-2 代码(ISO 3166-1 alpha-2),例如美国是US
。latlng
是一个数组,包含国家的纬度和经度,例如美国是[38.0, -97.0]
region
是国家所属的地区,例如美国是Americas
地图搭建
npm i leaflet
npm i @types/leaflet 添加类型支持
npm i react-leaflet
"use client";
import L, { LatLng, LatLngExpression } from "leaflet";
import { MapContainer, Marker, TileLayer } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import markerIcon2x from "leaflet/dist/images/marker-icon-2x.png";
import markerIcon from "leaflet/dist/images/marker-icon.png";
import markerShadow from "leaflet/dist/images/marker-shadow.png";
// @ts-ignore
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconUrl: markerIcon.src,
iconRetinaUrl: markerIcon2x.src,
shadowUrl: markerShadow.src,
});
interface MapProps {
center?: number[];
}
const Map: React.FC<MapProps> = ({ center }) => {
return (
<MapContainer
center={(center as LatLngExpression) || [51, -0.09]}
zoom={center ? 4 : 2}
scrollWheelZoom={false}
className="h-[35vh] rounded-lg"
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
></TileLayer>
{center && (
<Marker position={center as L.LatLngExpression}></Marker>
)}
</MapContainer>
);
};
export default Map;