今天通了一遍使用react进行用户管理系统的文档,以及跟随步骤实现了一遍,我大概梳理一下实现思路。
首先我们构建基本用户管理应用,需要数据库存储个人资料,我们先去supabase注册然后创建自己的数据库然后设置密码,然后去sql editor找到模板,user management starter创建一个空表profiles,这时候我们有了我们自己的数据库以及数据表,当然我们在创建表的时候也创建了相关的后端逻辑,也就是说supabase是一个自带后端的数据库,平台帮你一键搞定数据库、认证、权限、安全等基础设施,我们只需要调用平台接口,关注业务逻辑和前端开发。然后我们需要把官网创建的云端数据库拉取到本地,首先呢我们需要npm install -g supabase安装**Supabase CLI工具,**它可以初始化本地 Supabase 项目(supabase init)关联本地和云端项目(supabase link)拉取云端数据库结构(supabase db pull)本地启动 Supabase 开发环境(supabase start)等等。
然后下载完工具后,我们初始化本地supabase项目,也就是supabase init,然后我们需要登录supabase cli,让cil工具识别我们我们才可以supabase link执行这个把项目拉取到本地,这时候密码输入就算本地supabase和线上数据库连接了,然后supabase db pull拉取到本地supabase项目,我们就算是把云端的数据库表结构拿了过来。表结构包括表,函数,以及后端策略。
然后我们创建react模板的vite项目,新建.env.local文件引入数据库的url和anon key然后新建supabase.js我们引入supabase-js库然后加入代码
javascript
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
我们就相当于项目和数据库完全的连接了并且可以直接使用supabase-js里面的方法api以及后端逻辑。
那么剩下的步骤就是开发也就是写代码了。
这里我就都不写出来了我只写登入以及信息页面的代码。
javascript
import { useState } from 'react'
import { supabase } from './supabaseClient'
export default function Auth() {
const [loading, setLoading] = useState(false)
const [email, setEmail] = useState('')
const handleLogin = async (event) => {
event.preventDefault()
setLoading(true)
const { error } = await supabase.auth.signInWithOtp({ email })
if (error) {
alert(error.error_description || error.message)
} else {
alert('Check your email for the login link!')
}
setLoading(false)
}
return (
<div className="row flex flex-center">
<div className="col-6 form-widget">
<h1 className="header">Supabase + React</h1>
<p className="description">Sign in via magic link with your email below</p>
<form className="form-widget" onSubmit={handleLogin}>
<div>
<input
className="inputField"
type="email"
placeholder="Your email"
value={email}
required={true}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<button className={'button block'} disabled={loading}>
{loading ? <span>Loading</span> : <span>Send magic link</span>}
</button>
</div>
</form>
</div>
</div>
)
}
这是登录页面,下面是个人信息页面
TypeScript
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'
export default function Account({ session }) {
const [loading, setLoading] = useState(true)
const [username, setUsername] = useState(null)
const [website, setWebsite] = useState(null)
const [avatar_url, setAvatarUrl] = useState(null)
useEffect(() => {
let ignore = false
async function getProfile() {
setLoading(true)
const { user } = session
const { data, error } = await supabase
.from('profiles')
.select(username, website, avatar_url)
.eq('id', user.id)
.single()
if (!ignore) {
if (error) {
console.warn(error)
} else if (data) {
setUsername(data.username)
setWebsite(data.website)
setAvatarUrl(data.avatar_url)
}
}
setLoading(false)
}
getProfile()
return () => {
ignore = true
}
}, [session])
async function updateProfile(event, avatarUrl) {
event.preventDefault()
setLoading(true)
const { user } = session
const updates = {
id: user.id,
username,
website,
avatar_url: avatarUrl,
updated_at: new Date(),
}
const { error } = await supabase.from('profiles').upsert(updates)
if (error) {
alert(error.message)
} else {
setAvatarUrl(avatarUrl)
}
setLoading(false)
}
return (
<form onSubmit={updateProfile} className="form-widget">
<div>
<label htmlFor="email">Email</label>
<input id="email" type="text" value={session.user.email} disabled />
</div>
<div>
<label htmlFor="username">Name</label>
<input
id="username"
type="text"
required
value={username || ''}
onChange={(e) => setUsername(e.target.value)}
/>
</div>
<div>
<label htmlFor="website">Website</label>
<input
id="website"
type="url"
value={website || ''}
onChange={(e) => setWebsite(e.target.value)}
/>
</div>
<div>
<button className="button block primary" type="submit" disabled={loading}>
{loading ? 'Loading ...' : 'Update'}
</button>
</div>
<div>
<button className="button block" type="button" onClick={() => supabase.auth.signOut()}>
Sign Out
</button>
</div>
</form>
)
}
以及app页面
javascript
import './App.css'
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'
import Auth from './Auth'
import Account from './Account'
function App() {
const [session, setSession] = useState(null)
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setSession(session)
})
supabase.auth.onAuthStateChange((_event, session) => {
setSession(session)
})
}, [])
return (
<div className="container" style={{ padding: '50px 0 100px 0' }}>
{!session ? <Auth /> : <Account key={session.user.id} session={session} />}
</div>
)
}
export default App
现在我们总结一下思路,登录页面auth.jsx,关键是 await supabase.auth.signInWithOtp({ email })这个supabase自带的otp登录api,实现用户通过邮箱验收验证码的方式登录,无需密码,然后onChange ={(e ) => setEmail(e.target.value)}监听输入框内的内容,把内容存入状态,提交就可以了。
登录成功后session会返回,我们在app根组件中监听用supabase.auth.onAutnStateChange 来监听获取session,通过session是否返回也就是是否登录来判断渲染登录页还是账户页面
然后再账户页面,核心就是
javascript
const { data, error } = await supabase.from('profiles').select(username, website, avatar_url).eq('id', user.id).single()
使用useEffect在账户页面加载从profiles表中读取当前用户的username,website,avataer.url然后通过表单输入框 onchange监听实时更新username和website的值,点击更新按钮触发updataProfile函数调用await supabase.from('profiles').upsert(updates)更新数据库。
ok大概这就是核心思路,扩展的上传头像框可以去对应的文档练习,我觉得关键的思路这些已经齐全了,大家可以评论指点我一下,谢谢大家。