本文是graphql极简入门教程的第七篇,内容主要讲述前端对接已有的用户系统,实现前后端联调。
graphql极简入门教程目录:
- 第一篇:基于react和graphql-yoga搭建前后端,并实现一个hello world
- 第二篇:基于prisma及sqlite,通过playground创建及查询数据
- 第三篇:在react中执行graphql的新增和查询操作
- 第四篇:react添加路由导航、前后端搜索功能
- 第五篇:添加分页及排序功能
- 第六篇:后端编写用户登录及注册功能
- 第七篇:前端对接用户系统
- 第八篇:前后端接入github的Oauth系统
- 第九篇:graphql实时订阅
前端接入登录及注册功能
前端登录组件
在前面的章节中已经定义登录/注册的graphql语句,请同学们先不要看截图后面的代码,想一想你应该怎么写前端的语句:
这里放上之前在playground的截图,方便回忆:
![]()
下面就来揭晓答案,注册功能👇🏻
const signupMutation = `
mutation SignupMutation(
$email: String!
$password: String!
$name: String!
) {
signup(
email: $email
password: $password
name: $name
) {
token
}
}
`;
登录功能👇🏻
const loginMutation = `
mutation LoginMutation(
$email: String!
$password: String!
) {
login(email: $email, password: $password) {
token
}
}
`;
由于每次请求都会携带token信息,为了方便前端会将后端返回的token存入localstorage中,在每次请求时获取完后添加到请求的headers中。
因此需要新建一个constants.js文件,把localstorage的key值放到该文件中:
// 文件路径:src/constants.js
export const AUTH_TOKEN = 'auth-token';
接下来实现具体的登录/注册功能组件逻辑,在src/components文件夹下,新增Login.js组件:
// 文件路径:src/components/Login.js
import React, {useState} from 'react';
import {useNavigate} from 'react-router-dom';
import {AUTH_TOKEN} from "../constants";
import {useMutation} from "urql";
const signupMutation = `
mutation SignupMutation(
$email: String!
$password: String!
$name: String!
) {
signup(
email: $email
password: $password
name: $name
) {
token
}
}
`;
const loginMutation = `
mutation LoginMutation(
$email: String!
$password: String!
) {
login(email: $email, password: $password) {
token
}
}
`;
const Login = () => {
const navigate = useNavigate();
const [formState, setFormState] = useState({
login: true,
email: '',
password: '',
name: ''
});
const [,login] = useMutation(loginMutation);
const [,signup] = useMutation(signupMutation);
return (
<div>
<h4 className="mv3">
{formState.login ? 'Login' : 'Sign Up'}
</h4>
<div className="flex flex-column">
{!formState.login && (
<input
value={formState.name}
onChange={(e) =>
setFormState({
...formState,
name: e.target.value
})
}
type="text"
placeholder="Your name"
/>
)}
<input
value={formState.email}
onChange={(e) =>
setFormState({
...formState,
email: e.target.value
})
}
type="text"
placeholder="Your email address"
/>
<input
value={formState.password}
onChange={(e) =>
setFormState({
...formState,
password: e.target.value
})
}
type="password"
placeholder="Choose a safe password"
/>
</div>
<div className="flex mt3">
<button
className="pointer mr2 button"
onClick={() => {
if (formState.login) {
// ①
login({
email: formState.email,
password: formState.password
}).then(({ data: { login } }) => {
localStorage.setItem(AUTH_TOKEN, login.token);
navigate('/');
});
} else {
// ①
signup({
name: formState.name,
email: formState.email,
password: formState.password
}).then(({ data: { signup } }) => {
localStorage.setItem(AUTH_TOKEN, signup.token);
navigate('/');
})
}
}}
>
{formState.login ? 'login' : 'create account'}
</button>
<button
className="pointer button"
onClick={(e) =>
setFormState({
...formState,
login: !formState.login
})
}
>
{formState.login
? 'need to create an account?'
: 'already have an account?'}
</button>
</div>
</div>
);
};
export default Login;
相信经过前面使用useMutation钩子的经验,你应该不难理解上面的代码。
①中在执行登录/注册时,前端将用户输入的信息传递给useMutation钩子,在操作成功后将后端返回的token写入localstorage中,并跳转至首页。
现在,在src/components/App.js中,添加登录页面的路由:
// ...
import {Outlet, createBrowserRouter, RouterProvider, createRoutesFromElements, Route} from 'react-router-dom';
import Search from "./Search";
+ import Login from "./Login";
const AppLayout = () => (
<div className="center w85">
<Header />
<div className="ph3 pv1 background-gray">
<Outlet />
</div>
</div>
)
const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<AppLayout />}>
<Route path="/" element={<LinkList />} />
<Route path="/create" element={<CreateLink />} />
<Route path="/search" element={<Search />} />
+ <Route path="/login" element={<Login/>} />
</Route>
)
);
// ...
同时还需要在导航栏中,添加登录入口:
- 如果用户未登录,那么就不显示提交链接(
submit)的入口,并展示登录(login)入口; - 如果用户已经登录了,就展示登出(
logout)按钮,点击后会清除本地token缓存,并重定向到首页(/)。
打开src/components/Header.js文件,输入以下内容,完成上面的需求:
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { AUTH_TOKEN } from '../constants';
const Header = () => {
const navigate = useNavigate();
const authToken = localStorage.getItem(AUTH_TOKEN);
return (
<div className="flex pa1 justify-between nowrap orange">
<div className="flex flex-fixed black">
<Link to="/" className="no-underline black">
<div className="fw7 mr1">Hacker News</div>
</Link>
<Link to="/" className="ml1 no-underline black">
new
</Link>
<div className="ml1">|</div>
<Link
to="/search"
className="ml1 no-underline black"
>
search
</Link>
{authToken && (
<div className="flex">
<div className="ml1">|</div>
<Link
to="/create"
className="ml1 no-underline black"
>
submit
</Link>
</div>
)}
</div>
<div className="flex flex-fixed">
{authToken ? (
<div
className="ml1 pointer black"
onClick={() => {
localStorage.removeItem(AUTH_TOKEN);
navigate(`/`);
}}
>
logout
</div>
) : (
<Link
to="/login"
className="ml1 no-underline black"
>
login
</Link>
)}
</div>
</div>
);
};
export default Header;
在urql发送请求的headers中,需要额外添加authorization字段,方便后端识别用户信息。
在src/index.js文件中,添加上述逻辑:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './styles/index.css';
import App from './components/App';
import reportWebVitals from './reportWebVitals';
import { createClient, Provider } from 'urql';
import {AUTH_TOKEN} from "./constants";
const client = createClient({
url: 'http://localhost:4000/graphql',
fetchOptions: () => {
const token = localStorage.getItem(AUTH_TOKEN);
return {
// ①
headers: { authorization: token ? `Bearer ${token}` : '' },
};
},
});
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider value={client}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
在①中,将token以Bearer ${token}形式回传给后端。如果有忘记如何使用jwt的同学,也请参见阮一峰老师的入门链接👉🏻JSON Web Token 入门教程。
到这里就已经搞定了前端对接用户信息,接下来发送一条数据,来验证一下用户信息是否正确记录上。
创建带有用户信息的链接
打开http://localhost:3000/create页面,创建一条数据:
接下来,修改src/components/LinkList.js文件,添加请求postedBy字段的数据结构描述
const feedQuery = `
query FeedQuery($take: Int, $skip: Int, $orderBy: LinkOrderByInput) {
feed(take: $take, skip: $skip, orderBy: $orderBy) {
count
links {
id
createdAt
url
description
+ postedBy {
+ id
+ name
+ }
}
}
}
`;
此时刷新页面,打开控制台,就可以看到后端回传的postedBy字段的具体信息啦!
至此我们关于用户系统的接入已经全部完成!恭喜你已经更进一步了解了graphql了
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情