本文是graphql极简入门教程的第四篇,本篇内容主要讲述为react添加路由导航、完成前后端搜索功能
graphql极简入门教程目录:
- 第一篇:基于react和graphql-yoga搭建前后端,并实现一个hello world
- 第二篇:基于prisma及sqlite,通过playground创建及查询数据
- 第三篇:在react中执行graphql的新增和查询操作
- 第四篇:react添加路由导航、前后端搜索功能
- 第五篇:添加分页及排序功能
- 第六篇:后端编写用户登录及注册功能
- 第七篇:前端对接用户系统
- 第八篇:前后端接入github的Oauth系统
- 第九篇:graphql实时订阅
添加路由
react路由采用react-router6,因此需要安装react-router-dom依赖:
npm install react-router-dom@6.8
页面内添加顶部导航,在src/components文件夹下,新建Header.js文件,在文件内编写顶部导航代码:
import React from 'react';
import { Link } from 'react-router-dom';
const Header = () => {
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="/create"
className="ml1 no-underline black"
>
submit
</Link>
</div>
</div>
);
};
export default Header;
在src/components/App.js文件中引入导航组件,并配置不同的路由:
由于react-routerv6.4后的版本对api进行了更新,导致用法与以前的版本有所不同,具体可以参考官网文档
import React from 'react';
import CreateLink from './CreateLink';
import Header from './Header';
import LinkList from './LinkList';
import {Outlet, createBrowserRouter, RouterProvider, createRoutesFromElements, Route} from 'react-router-dom';
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>
)
);
const App = () => (
<RouterProvider router={router} />
);
export default App;
此时打开http://localhost:3000页面,可以看到当前的展示效果:
完成了导航后,还需要完善创建链接页面:
如果创建链接成功后,跳回首页,需要对src/components/CreateLink.js文件做如下改动:
import React, { useState } from 'react';
import {useMutation} from "urql";
+ import { useNavigate } from 'react-router-dom';
export const createLinkMutation = `
mutation PostMutation(
$description: String!
$url: String!
) {
post(description: $description, url: $url) {
id
createdAt
url
description
}
}
`;
const CreateLink = () => {
// ①
+ const navigate = useNavigate();
const [createLinkResult, createLink] = useMutation(createLinkMutation);
const [formState, setFormState] = useState({
description: '',
url: ''
});
const { data, fetching, error } = createLinkResult;
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
- createLink(formState)
// ②
+ createLink(formState).then(() => navigate('/'));
}}
>
<div className="flex flex-column mt3">
<input
className="mb2"
value={formState.description}
onChange={(e) =>
setFormState({
...formState,
description: e.target.value
})
}
type="text"
placeholder="A description for the link"
/>
<input
className="mb2"
value={formState.url}
onChange={(e) =>
setFormState({
...formState,
url: e.target.value
})
}
type="text"
placeholder="The URL for the link"
/>
</div>
<button disabled={fetching} type="submit">Submit {fetching && '(sending...)'}</button>
</form>
</div>
);
};
export default CreateLink;
在①中,使用react-router提供的useNavigate钩子跳转页面。
在②中,指定创建成功后,跳转到首页。
打开http://localhost:3000/create创建链接页面,测试一下效果:
创建成功后,自动跳回了首页,并且最新的数据已经展示出来:
添加搜索
前端搜索语句定义
在搜索时,需要将用户输入的搜索值透传给graphql,因此前端的graphql的类型定义如下:
const feedSearchQuery = `
query FeedSearchQuery($filter: String!) {
feed(filter: $filter) {
links {
id
url
description
createdAt
}
}
}
`;
在查询时,传入了一个不为空的filter字符串,后端根据该字符串进行搜索。
后端数据类型定义
前端定义完成后,现在开始定义后端的数据类型,修改src/server/schema.graphql文件
type Query {
- feed: Feed!
+ feed(filter: String): Feed!
}
type Link {
id: ID!
url: String!
description: String!
createdAt: DateTime!
}
type Feed {
links: [Link!]!
}
scalar DateTime
type Mutation {
post(url: String!, description: String!): Link!
}
在这里filter没有强制传入参数的原因是,有可能用户并没有输入搜索,此时还需要能够正常查询,因此在后端不进行强行校验。
添加后端执行逻辑
接着添加具体的搜索逻辑,对src/server/resolvers/Query.js文件进行更改:
async function feed(parent, args, context) {
+ const where = args.filter
+ ? {
+ OR: [
+ { description: { contains: args.filter } },
+ { url: { contains: args.filter } }
+ ]
+ }
+ : {};
- const links = await context.prisma.link.findMany();
+ const links = await context.prisma.link.findMany({ where });
return {
links,
};
}
module.exports = {
feed
};
在上面的代码中,添加了搜索语句,针对description及url两个字段进行过滤,如果任意(OR)一个字段包含(contains)用户输入的内容(args.filter),就返回该结果。
前端添加搜索组件
在src/components目录下,新建Search.js文件,添加如下内容:
// 文件路径:src/components/Search.js
import React, { useState } from 'react';
import Link from './Link';
import {useQuery} from "urql";
export const feedSearchQuery = `
query FeedSearchQuery($filter: String!) {
feed(filter: $filter) {
links {
id
url
description
createdAt
}
}
}
`;
const Search = () => {
// ①
const [userInput, setUserInput] = useState('');
// ②
const [searchFilter, setSearchFilter] = useState('');
const [result] = useQuery({
query: feedSearchQuery,
// ③
variables: { filter: searchFilter },
})
const { data } = result;
return (
<>
<div>
Search
<input
type="text"
// ①
onChange={(e) => setUserInput(e.target.value)}
/>
// ②
<button onClick={() => { setSearchFilter(userInput) }}>OK</button>
</div>
{data &&
data.feed.links.map((link, index) => (
<Link key={link.id} link={link} index={index} />
))}
</>
);
};
export default Search;
在①中,输入框监听用户输入的数据并进行存储
当用户点击了OK按钮,就将用户输入的最终内容同步到searchFilter中
在③中,接收到更新的搜索内容后,urql将会自动重新请求数据,将用户需要搜索的内容查询出来。
前端添加路由及导航
在src/components/App.js文件中添加路由,改动如下:
import React from 'react';
import CreateLink from './CreateLink';
import Header from './Header';
import LinkList from './LinkList';
import {Outlet, createBrowserRouter, RouterProvider, createRoutesFromElements, Route} from 'react-router-dom';
+ import Search from "./Search";
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>
)
);
const App = () => (
<RouterProvider router={router} />
);
export default App;
在src/components/Header.js文件中添加导航,改动如下:
import React from 'react';
import { Link } from 'react-router-dom';
const Header = () => {
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="/create"
className="ml1 no-underline black"
>
submit
</Link>
+ <div className="ml1">|</div>
+ <Link
+ to="/search"
+ className="ml1 no-underline black"
+ >
+ search
+ </Link>
</div>
</div>
);
};
export default Header;
打开http://localhost:4000/search页面,输入搜索内容juejin,你可以得到下面的结果:
本章我们完成了添加前端路由导航及前后端搜索功能,接下来的章节中我们将介绍如何针对列表数据进行分页。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情