翻译 | 《JavaScript Everywhere》第16章 建立,读取,更新和删除操做

翻译 | 《JavaScript Everywhere》第16章 建立,读取,更新和删除操做php

写在最前面

你们好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。前端

为了提升你们的阅读体验,对语句的结构和内容略有调整。若是发现本文中有存在瑕疵的地方,或者你有任何意见或者建议,能够在评论区留言,或者加个人微信:code_maomao,欢迎相互沟通交流学习。react

(σ゚∀゚)σ..:*☆哎哟不错哦数据库

第16章 建立,读取,更新和删除操做

我喜欢纸质笔记本,几乎一直都随身携带着。一般,它们相对便宜,我很快就写满了临时的想法。不久前,我购买了价格更高的精装本笔记本,上面有精美的封面和精美的纸本。购买时,我对笔记本中将要出现的草图和计划抱有很大的野心,可是它在个人办公桌上呆了几个月。最终,我将其放在架子上,回到了个人标准笔记本电脑品牌。缓存

就像个人笔记本同样,咱们的应用仅在用户可以使用时才有用。你可能会从咱们的API开发中回想起Notedly应用程序是一个“ CRUD”(建立,读取,更新和删除)应用程序。通过身份验证的用户能够建立新笔记、阅读笔记、更新笔记的内容或笔记做为收藏夹的状态以及删除笔记。在本章中,咱们将在Web用户界面中实现全部这些功能。为了完成这些任务,咱们将编写GraphQL请求和查询。服务器

建立新笔记

当前,咱们能够查看笔记,但没法建立笔记。这相似于没有笔的笔记本。让咱们为用户添加建立新笔记的功能。咱们将经过建立一个用户能够在其中写笔记的textarea形式。微信

当用户提交表单时,咱们将执行GraphQL更改以在数据库中建立笔记。react-router

首先,让咱们在src/pages/new.js中建立NewNote组件:app

import React, { useEffect } from 'react';
import { useMutation, gql } from '@apollo/client';
const NewNote = props => {
useEffect(() => {
// update the document title
document.title = 'New Note — Notedly';
});
return <div>New note</div>;
};
export default NewNote;

接下来,让咱们在src/pages/index.js文件中设置新路由:dom

// import the NewNote route component
import NewNote from './new';
// add a private route to our list of routes, within the
<PrivateRoute path="/new" component={NewNote} />

咱们知道,咱们将建立新笔记并更新现有笔记。为了适应这种行为,让咱们建立一个名为NoteForm的新组件,该组件将用做笔记表单编辑的标记和React状态。

咱们将在src/components/NoteForm.js中建立一个新文件。该组件将由一个包含文本区域以及一些最小样式的表单元素组成。该功能将很是相似于咱们的UserForm组件:

import React, { useState } from 'react';
import styled from 'styled-components';
import Button from './Button';
const Wrapper = styled.div`
height: 100%;
`;
const Form = styled.form`
height: 100%;
`;
const TextArea = styled.textarea`
width: 100%;
height: 90%;
`;
const NoteForm = props => {
// set the default state of the form
const [value, setValue] = useState({ content: props.content || '' });
// update the state when a user types in the form
const onChange = event => {
setValue({
...value,
[event.target.name]: event.target.value
});
};
return (
<Wrapper> <Form onSubmit={e => {
e.preventDefault();
props.action({
variables: {
...values
}
});
}}
> <TextArea required type="text" name="content" placeholder="Note content" value={value.content} onChange={onChange} /> <Button type="submit">Save</Button> </Form> </Wrapper> );
};
export default NoteForm;

下一步,咱们将须要参考在NewNote页面组件的NoteForm成分。在src/pages/new.js中:

import React, { useEffect } from 'react';
import { useMutation, gql } from '@apollo/client';
// import the NoteForm component
import NoteForm from '../components/NoteForm';
const NewNote = props => {
useEffect(() => {
// update the document title
document.title = 'New Note — Notedly';
});
return <NoteForm />;
};
export default NewNote;

有了这些更新,切换至http://localhost:1234/new将显示咱们的效果(图16-1)。

16-1咱们的NewNote组件为用户提供了一个较大的文本区域和保存按钮

完成表格后,咱们能够开始编写咱们的修改以建立新笔记。

src/pages/new.js中:

import React, { useEffect } from 'react';
import { useMutation, gql } from '@apollo/client';
import NoteForm from '../components/NoteForm';
// our new note query
const NEW_NOTE = gql`
mutation newNote($content: String!) {
newNote(content: $content) {
id
content
createdAt
favoriteCount
favoritedBy {
id
username
}
author {
username
id
}
}
}
`;
const NewNote = props => {
useEffect(() => {
// update the document title
document.title = 'New Note — Notedly';
});
const [data, { loading, error }] = useMutation(NEW_NOTE, {
onCompleted: data => {
// when complete, redirect the user to the note page
props.history.push(`note/${data.newNote.id}`);
}
});
return (
<React.Fragment>
{/* as the mutation is loading, display a loading message*/}
{loading && <p>Loading...</p>}
{/* if there is an error, display a error message*/}
{error && <p>Error saving the note</p>}
{/* the form component, passing the mutation data as a prop */}
<NoteForm action={data} />
</React.Fragment>
);
};
export default NewNote;

在前面的代码中,提交表单时,咱们执行newNote修改。若是修改为功,则将用户重定向到单个笔记页面。你可能会注意到newNote修改请求了不少数据。这与笔记修改所请求的数据匹配,理想地更新了Apollo的缓存以快速切换到各个笔记组件。

如前所述,Apollo积极地缓存咱们的查询,这有助于加快应用程序的切换。不幸的是,这也意味着用户能够访问页面,而看不到他们刚刚进行的更新。咱们能够手动更新Apollo的缓存,可是更简单的方法是使用ApollorefetchQueries功能可在执行修改时有意地更新缓存。

为此,咱们须要访问预先编写的查询。到目前为止,咱们一直将它们包含在组件文件的顶部,但让咱们将其移动到本身的query.js文件中。在/src/gql/query.js中建立一个新文件,并添加咱们的每一个笔记查询以及IS_LOGGED_IN查询:

import { gql } from '@apollo/client';
const GET_NOTES = gql`
query noteFeed($cursor: String) {
noteFeed(cursor: $cursor) {
cursor
hasNextPage
notes {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
}
`;
const GET_NOTE = gql`
query note($id: ID!) {
note(id: $id) {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
`;
const IS_LOGGED_IN = gql`
{
isLoggedIn @client
}
`;
export { GET_NOTES, GET_NOTE, IS_LOGGED_IN };

可重用的查询和修改

展望将来,咱们将全部查询和修改都与组件分开,这将使咱们可以轻松地在应用程序中重用它们,而且对于在测试期间mocking

如今在src/pages/new.js中,咱们能够经过导入查询并添加refetchQueries选项来请求咱们的修改从新获取GET_NOTES查询:

// import the query
import { GET_NOTES } from '../gql/query';
// within the NewNote component update the mutation
//everything else stays the same
const NewNote = props => {
useEffect(() => {
// update the document title
document.title = 'New Note — Notedly';
});
const [data, { loading, error }] = useMutation(NEW_NOTE, {
// refetch the GET_NOTES query to update the cache
refetchQueries: [{ query: GET_NOTES }],
onCompleted: data => {
// when complete, redirect the user to the note page
props.history.push(`note/${data.newNote.id}`);
}
});
return (
<React.Fragment>
{/* as the mutation is loading, display a loading message*/}
{loading && <p>Loading...</p>}
{/* if there is an error, display a error message*/}
{error && <p>Error saving the note</p>}
{/* the form component, passing the mutation data as a prop */}
<NoteForm action={data} />
</React.Fragment>
);
};

咱们的最后一步是将连接添加到咱们的/new页面,以便用户能够轻松切换到该页面。在src/components/Navigation.js文件中,添加一个新的连接项,以下所示:

<li>
<Link to="/new">New</Link>
</li>

这样,咱们的用户就能够切换到新的笔记页面,输入笔记,而后将笔记保存到数据库中。

阅读用户说明

咱们的应用程序当前可以读取咱们的笔记摘要以及单个笔记,可是咱们尚未查询通过身份验证的用户的笔记。让咱们编写两个GraphQL查询来建立用户及其收藏夹的提要。

src/gql/query.js中,添加GET_MY_NOTES查询并更新导出,以下所示:

// add the GET_MY_NOTES query
const GET_MY_NOTES = gql`
query me {
me {
id
username
notes {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
}
`;
// update to include GET_MY_NOTES
export { GET_NOTES, GET_NOTE, IS_LOGGED_IN, GET_MY_NOTES };

如今在src/pages/mynotes.js中,导入查询并使用NoteFeed组件显示笔记:

import React, { useEffect } from 'react';
import { useQuery, gql } from '@apollo/client';
import NoteFeed from '../components/NoteFeed';
import { GET_MY_NOTES } from '../gql/query';
const MyNotes = () => {
useEffect(() => {
// update the document title
document.title = 'My Notes — Notedly';
});
const { loading, error, data } = useQuery(GET_MY_NOTES);
// if the data is loading, our app will display a loading message
if (loading) return 'Loading...';
// if there is an error fetching the data, display an error message
if (error) return `Error! ${error.message}`;
// if the query is successful and there are notes, return the feed of notes
// else if the query is successful and there aren't notes, display a message
if (data.me.notes.length !== 0) {
return <NoteFeed notes={data.me.notes} />;
} else {
return <p>No notes yet</p>;
}
};
export default MyNotes;

咱们能够重复此过程以建立“收藏夹”页面。首先,在src/gql/query.js中:

// add the GET_MY_FAVORITES query
const GET_MY_FAVORITES = gql`
query me {
me {
id
username
favorites {
id
createdAt
content
favoriteCount
author {
username
id
avatar
}
}
}
}
`;
// update to include GET_MY_FAVORITES
export { GET_NOTES, GET_NOTE, IS_LOGGED_IN, GET_MY_NOTES, GET_MY_FAVORITES };

如今,在src/pages/favorites.js中:

import React, { useEffect } from 'react';
import { useQuery, gql } from '@apollo/client';
import NoteFeed from '../components/NoteFeed';
// import the query
import { GET_MY_FAVORITES } from '../gql/query';
const Favorites = () => {
useEffect(() => {
// update the document title
document.title = 'Favorites — Notedly';
});
const { loading, error, data } = useQuery(GET_MY_FAVORITES);
// if the data is loading, our app will display a loading message
if (loading) return 'Loading...';
// if there is an error fetching the data, display an error message
if (error) return `Error! ${error.message}`;
// if the query is successful and there are notes, return the feed of notes
// else if the query is successful and there aren't notes, display a message
if (data.me.favorites.length !== 0) {
return <NoteFeed notes={data.me.favorites} />;
} else {
return <p>No favorites yet</p>;
}
};
export default Favorites;

最后,让咱们更新src/pages/new.js文件以从新获取GET_MY_NOTES查询,以确保在建立笔记时更新了用户笔记的缓存列表。在src/pages/new.js中,首先更新GraphQL查询import语句:

import { GET_MY_NOTES, GET_NOTES } from '../gql/query';

而后更新修改:

const [data, { loading, error }] = useMutation(NEW_NOTE, {
// refetch the GET_NOTES and GET_MY_NOTES queries to update the cache
refetchQueries: [{ query: GET_MY_NOTES }, { query: GET_NOTES }],
onCompleted: data => {
// when complete, redirect the user to the note page
props.history.push(`note/${data.newNote.id}`);
}
});

经过这些更改,咱们如今能够在咱们的应用程序中执行全部读取操做。

更新说明

当前,用户一旦写了笔记,便没法对其进行更新。为了解决这个问题,咱们但愿在应用程序中启用笔记编辑。咱们的GraphQL API具备updateNote修改,它接受笔记ID和内容做为参数。若是笔记存在于数据库中,则修改将使用修改中发送的内容更新存储的内容。

在咱们的应用程序中,咱们能够在/ edit/NOTE_ID处建立一条路由,将现有笔记内容放置在textarea表单中。当用户单击“保存”时,咱们将提交表单并执行updateNote更改。

让咱们建立一条新路由,在该路由中将编辑咱们的笔记。首先,咱们可使咱们的一个重复的src/pages/ note.js页并将其命名为edit.js。目前,该页面仅显示笔记。

src/pages/edit.js中:

import React from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
// import the Note component
import Note from '../components/Note';
// import the GET_NOTE query
import { GET_NOTE } from '../gql/query';
const EditNote = props => {
// store the id found in the url as a variable
const id = props.match.params.id;
// define our note query
const { loading, error, data } = useQuery(GET_NOTE, { variables: { id } });
// if the data is loading, display a loading message
if (loading) return 'Loading...';
// if there is an error fetching the data, display an error message
if (error) return <p>Error! Note not found</p>;
// if successful, pass the data to the note component
return <Note note={data.note} />;
};
export default EditNote;

如今,咱们能够经过将页面添加到src/pages/index.js中的路由来使页面可切换:

// import the edit page component
import EditNote from './edit';
// add a new private route that accepts an :id parameter
<PrivateRoute path="/edit/:id" component={EditNote} />

这样,若是你切换到/note/ID处的笔记页面并将其交换为/edit/ID,你将看到笔记的改变。让咱们对其进行更改,以使其显示在表单的textarea中显示的笔记内容。

src/pages/edit.js中,删除Note组件的import语句,并将其替换为NoteForm组件:

// import the NoteForm component
import NoteForm from '../components/NoteForm';

如今,咱们能够更新EditNote组件来使用咱们的编辑表单。咱们可使用content属性将笔记的内容传递给表单组件。虽然咱们的GraphQL修改仅接受原做者的更新,但咱们也能够只将表单显示的范围限制为笔记的做者,以免混淆其余用户。

首先,将新查询添加到src/gql/query.js文件中,以获取当前用户,其用户ID以及收藏的笔记ID的列表:

// add GET_ME to our queries
const GET_ME = gql`
query me {
me {
id
favorites {
id
}
}
}
`;
// update to include GET_ME
export {
GET_NOTES,
GET_NOTE,
GET_MY_NOTES,
GET_MY_FAVORITES,
GET_ME,
IS_LOGGED_IN
};

src/pages/edit.js中,导入GET_ME查询并包括用户检查:

import React from 'react';
import { useMutation, useQuery } from '@apollo/client';
// import the NoteForm component
import NoteForm from '../components/NoteForm';
import { GET_NOTE, GET_ME } from '../gql/query';
import { EDIT_NOTE } from '../gql/mutation';
const EditNote = props => {
// store the id found in the url as a variable
const id = props.match.params.id;
// define our note query
const { loading, error, data } = useQuery(GET_NOTE, { variables: { id } });
// fetch the current user's data
const { data: userdata } = useQuery(GET_ME);
// if the data is loading, display a loading message
if (loading) return 'Loading...';
// if there is an error fetching the data, display an error message
if (error) return <p>Error! Note not found</p>;
// if the current user and the author of the note do not match
if (userdata.me.id !== data.note.author.id) {
return <p>You do not have access to edit this note</p>;
}
// pass the data to the form component
return <NoteForm content={data.note.content} />;
};

如今,咱们能够编辑表单中的笔记,可是单击按钮尚不能保存咱们的更改。让咱们写咱们的GraphQLupdateNote修改。与查询文件相似,让咱们建立一个文件来保存咱们的修改。在src/gql/mutation中,添加如下内容:

import { gql } from '@apollo/client';
const EDIT_NOTE = gql`
mutation updateNote($id: ID!, $content: String!) {
updateNote(id: $id, content: $content) {
id
content
createdAt
favoriteCount
favoritedBy {
id
username
}
author {
username
id
}
}
}
`;
export { EDIT_NOTE };

编写好修改后,咱们能够导入它并更新咱们的组件代码,以在单击按钮时调用该修改。为此,咱们将添加一个useMutation挂钩。修改完成后,咱们会将用户重定向到笔记页面。

// import the mutation
import { EDIT_NOTE } from '../gql/mutation';
const EditNote = props => {
// store the id found in the url as a variable
const id = props.match.params.id;
// define our note query
const { loading, error, data } = useQuery(GET_NOTE, { variables: { id } });
// fetch the current user's data
const { data: userdata } = useQuery(GET_ME);
// define our mutation
const [editNote] = useMutation(EDIT_NOTE, {
variables: {
id
},
onCompleted: () => {
props.history.push(`/note/${id}`);
}
});
// if the data is loading, display a loading message
if (loading) return 'Loading...';
// if there is an error fetching the data, display an error message
if (error) return <p>Error!</p>;
// if the current user and the author of the note do not match
if (userdata.me.id !== data.note.author.id) {
return <p>You do not have access to edit this note</p>;
}
// pass the data and mutation to the form component
return <NoteForm content={data.note.content} action={editNote} />;
};
export default EditNote;

最后,咱们只想向用户显示“编辑”连接,但前提是用户是笔记的做者。在咱们的应用程序中,咱们将须要检查以确保当前用户的ID与笔记做者的ID相匹配。为了实现此行为,咱们将涉及几个组件。

如今,咱们能够直接在Note组件中实现咱们的功能,让咱们在src/components/NoteUser.js上建立一个专门用于登陆用户交互的组件。在这个React组件中,咱们将对当前用户ID执行GraphQL查询,并提供一个指向编辑页面的可路由连接。有了这些信息,咱们就能够开始导入所需的库并设置一个新的React组件。在React组件内,咱们将包含一个编辑连接,该连接会将用户引导至笔记的编辑页面。如今,不管笔记的全部者是谁,用户都将看到此连接。

以下更新src/components/NoteUser.js

import React from 'react';
import { useQuery, gql } from '@apollo/client';
import { Link } from 'react-router-dom';
const NoteUser = props => {
return <Link to={`/edit/${props.note.id}`}>Edit</Link>;
};
export default NoteUser;

接下来,咱们将更新Note组件以执行本地isLoggedIn状态查询。

而后,咱们能够根据用户的登陆状态有条件地呈现NoteUser组件。

首先,咱们导入GraphQL库与NoteUser组件一块儿执行查询。在src/components/Note.js中,在文件顶部添加如下内容:

import { useQuery } from '@apollo/client';
// import logged in user UI components
import NoteUser from './NoteUser';
// import the IS_LOGGED_IN local query
import { IS_LOGGED_IN } from '../gql/query';

如今,咱们能够更新咱们的JSX组件以检查登陆状态。若是用户已登陆,咱们将显示NoteUser组件;不然,咱们将显示收藏夹计数。

const Note = ({ note }) => {
const { loading, error, data } = useQuery(IS_LOGGED_IN);
// if the data is loading, display a loading message
if (loading) return <p>Loading...</p>;
// if there is an error fetching the data, display an error message
if (error) return <p>Error!</p>;
return (
<StyledNote>
<MetaData>
<MetaInfo>
<img src={note.author.avatar} alt={`${note.author.username} avatar`} height="50px" />
</MetaInfo>
<MetaInfo>
<em>by</em> {note.author.username} <br />
{format(note.createdAt, 'MMM Do YYYY')}
</MetaInfo>
{data.isLoggedIn ? (
<UserActions>
<NoteUser note={note} />
</UserActions>
) : (
<UserActions>
<em>Favorites:</em> {note.favoriteCount}
</UserActions>
)}
</MetaData>
<ReactMarkdown source={note.content} />
</StyledNote>
);
};

未经身份验证的编辑

尽管咱们将在UI中隐藏编辑连接,但用户仍然能够切换到笔记的编辑屏幕,而无需成为笔记全部者。值得庆幸的是,咱们的GraphQL API旨在防止笔记全部者之外的任何人编辑笔记的内容。咱们不会在本书中进行介绍,可是一个不错的附加步骤是更新src/pages/edit.js组件以重定向用户(若是他们不是笔记全部者)。

进行此更改后,登陆用户能够在每一个笔记的顶部看到一个编辑连接。单击该连接将切换到一个编辑表单,而无论笔记的全部者是谁。让咱们经过更新NoteUser组件来查询当前用户的ID并仅在其与笔记做者的ID匹配时显示编辑连接来解决此问题。

首先在src/components/NoteUser.js中,添加如下内容:

import React from 'react'; import { useQuery } from '@apollo/client'; import { Link } from 'react-router-dom'; // import our GET_ME query
import { GET_ME } from '../gql/query'; const NoteUser = props => { const { loading, error, data } = useQuery(GET_ME); // if the data is loading, display a loading message
if (loading) return <p>Loading...</p>; // if there is an error fetching the data, display an error message
if (error) return <p>Error!</p>; return (
<React.Fragment>
Favorites: {props.note.favoriteCount}
<br />
{data.me.id === props.note.author.id && (
<React.Fragment>
<Link to={`/edit/${props.note.id}`}>Edit</Link>
</React.Fragment>
)}
</React.Fragment>
);
};
export default NoteUser;

进行此更改后,只有笔记的原始做者才能在UI中看到编辑连接(图16-2)。

16-2。只有笔记的做者才能看到编辑连接

删除笔记

咱们的CRUD应用程序仍然缺乏删除笔记的功能。咱们能够编写一个UI按钮组件,单击该组件将执行GraphQL修改,从而删除笔记。让咱们从src/components/DeleteNote.js建立一个新组件开始。因为咱们将在不可路由的组件内执行重定向,所以咱们将使用React RouterwithRouter高阶组件:

import React from 'react';
import { useMutation } from '@apollo/client';
import { withRouter } from 'react-router-dom';
import ButtonAsLink from './ButtonAsLink';
const DeleteNote = props => {
return <ButtonAsLink>Delete Note</ButtonAsLink>;
};
export default withRouter(DeleteNote);

如今,咱们能够编写咱们的修改了。咱们的GraphQL API具备deleteNote修改,若是删除了笔记,则返回布尔值true。修改完成后,咱们会将用户重定向到应用程序的/ mynotes页面。

首先,在src/gql/mutation.js中,按以下所示编写修改:

const DELETE_NOTE = gql`
mutation deleteNote($id: ID!) {
deleteNote(id: $id)
}
`;
// update to include DELETE_NOTE
export { EDIT_NOTE, DELETE_NOTE };

如今在src/components/DeleteNote中,添加如下内容:

import React from 'react';
import { useMutation } from '@apollo/client';
import { withRouter } from 'react-router-dom';
import ButtonAsLink from './ButtonAsLink';
// import the DELETE_NOTE mutation
import { DELETE_NOTE } from '../gql/mutation';
// import queries to refetch after note deletion
import { GET_MY_NOTES, GET_NOTES } from '../gql/query';
const DeleteNote = props => {
const [deleteNote] = useMutation(DELETE_NOTE, {
variables: {
id: props.noteId
},
// refetch the note list queries to update the cache
refetchQueries: [{ query: GET_MY_NOTES, GET_NOTES }],
onCompleted: data => {
// redirect the user to the "my notes" page
props.history.push('/mynotes');
}
});
return <ButtonAsLink onClick={deleteNote}>Delete Note</ButtonAsLink>;
};
export default withRouter(DeleteNote);

如今,咱们能够在src/components/NoteUser.js文件中导入新的DeleteNote组件,仅将其显示给笔记的做者:

import React from 'react';
import { useQuery } from '@apollo/client';
import { Link } from 'react-router-dom';
import { GET_ME } from '../gql/query';
// import the DeleteNote component
import DeleteNote from './DeleteNote';
const NoteUser = props => {
const { loading, error, data } = useQuery(GET_ME);
// if the data is loading, display a loading message
if (loading) return <p>Loading...</p>;
// if there is an error fetching the data, display an error message
if (error) return <p>Error!</p>;
return (
<React.Fragment>
Favorites: {props.note.favoriteCount} <br />
{data.me.id === props.note.author.id && (
<React.Fragment>
<Link to={`/edit/${props.note.id}`}>Edit</Link> <br />
<DeleteNote noteId={props.note.id} />
</React.Fragment>
)}
</React.Fragment>
);
};
export default NoteUser;

写入此修改后,登陆用户如今能够经过单击按钮删除笔记。

切换收藏夹

咱们的应用程序缺乏的最后一个用户功能是可以添加和删除“收藏夹”笔记。让咱们遵循为该功能建立组件并将其集成到咱们的应用程序中的模式。首先,在src/components/FavoriteNote.js中建立一个新组件:

import React, { useState } from 'react';
import { useMutation } from '@apollo/client';
import ButtonAsLink from './ButtonAsLink';
const FavoriteNote = props => {
return <ButtonAsLink>Add to favorites</ButtonAsLink>;
};
export default FavoriteNote;

在添加任何功能以前,让咱们继续将该组件合并到咱们的src/components/NoteUser.js组件中。首先,导入组件:

import FavoriteNote from './FavoriteNote';

如今,在咱们的JSX中,包括了对该组件的引用。你可能还记得,当咱们编写GET_ME查询时,咱们包括了一个喜好的笔记ID列表,咱们将在这里使用它:

return (
<React.Fragment>
<FavoriteNote me={data.me} noteId={props.note.id} favoriteCount={props.note.favoriteCount} />
<br />
{data.me.id === props.note.author.id && (
<React.Fragment>
<Link to={`/edit/${props.note.id}`}>Edit</Link> <br />
<DeleteNote noteId={props.note.id} />
</React.Fragment>
)}
</React.Fragment>
);

你会注意到,咱们正在将三个属性传递给FavoriteNote组件。首先是咱们的个人数据,其中包括当前用户的ID,以及由用户收藏笔记的列表。第二,当前笔记的noteID。最后是favoriteCount,它是当前用户收藏夹的总数。

如今,咱们能够返回src/components/FavoriteNote.js文件。在此文件中,咱们将存储当前收藏夹数做为状态,并检查当前笔记ID是否在用户收藏夹的现有列表中。咱们将根据用户收藏夹的状态更改用户看到的文本。当用户单击按钮时,它将调用咱们的toggleFavorite修改,该修改将在用户列表中添加或删除收藏夹。让咱们首先更新组件以使用状态来控制点击功能。

const FavoriteNote = props => {
// store the note's favorite count as state
const [count, setCount] = useState(props.favoriteCount);
// store if the user has favorited the note as state
const [favorited, setFavorited] = useState(
// check if the note exists in the user favorites list
props.me.favorites.filter(note => note.id === props.noteId).length > 0
);
return (
<React.Fragment> {favorited ? ( <ButtonAsLink onClick={() => {
setFavorited(false);
setCount(count - 1);
}}
>
Remove Favorite </ButtonAsLink> ) : ( <ButtonAsLink onClick={() => {
setFavorited(true);
setCount(count + 1);
}}
>
Add Favorite </ButtonAsLink> )}
: {count} </React.Fragment> );
};

经过前面的更改,咱们将在用户单击时更新状态,但还没有调用GraphQL修改。让咱们经过编写修改并将其添加到组件中来完成此组件。结果显示为图16-3

src/gql/mutation.js中:

// add the TOGGLE_FAVORITE mutation
const TOGGLE_FAVORITE = gql`
mutation toggleFavorite($id: ID!) {
toggleFavorite(id: $id) {
id
favoriteCount
}
}
`;
// update to include TOGGLE_FAVORITE
export { EDIT_NOTE, DELETE_NOTE, TOGGLE_FAVORITE };

src/components/FavoriteNote.js中:

import React, { useState } from 'react';
import { useMutation } from '@apollo/client';
import ButtonAsLink from './ButtonAsLink';
// the TOGGLE_FAVORITE mutation
import { TOGGLE_FAVORITE } from '../gql/mutation';
// add the GET_MY_FAVORITES query to refetch
import { GET_MY_FAVORITES } from '../gql/query';
const FavoriteNote = props => {
// store the note's favorite count as state
const [count, setCount] = useState(props.favoriteCount);
// store if the user has favorited the note as state
const [favorited, setFavorited] = useState(
// check if the note exists in the user favorites list
props.me.favorites.filter(note => note.id === props.noteId).length > 0
);
// toggleFavorite mutation hook
const [toggleFavorite] = useMutation(TOGGLE_FAVORITE, {
variables: {
id: props.noteId
},
// refetch the GET_MY_FAVORITES query to update the cache
refetchQueries: [{ query: GET_MY_FAVORITES }]
});
// if the user has favorited the note, display the option to remove the favorite
// else, display the option to add as a favorite
return (
<React.Fragment> {favorited ? ( <ButtonAsLink onClick={() => {
toggleFavorite();
setFavorited(false);
setCount(count - 1);
}}
>
Remove Favorite </ButtonAsLink> ) : ( <ButtonAsLink onClick={() => {
toggleFavorite();
setFavorited(true);
setCount(count + 1);
}}
>
Add Favorite </ButtonAsLink> )}
: {count} </React.Fragment> );
};
export default FavoriteNote;

16-3。登陆的用户将可以建立,阅读,更新和删除笔记

结论

在本章中,咱们已将站点变成功能齐全的CRUD(建立,读取,更新,删除)应用程序。如今,咱们能够根据已登陆用户的状态来实现GraphQL查询和修改。构建集成CRUD用户交互的用户界面的能力将为构建各类Web应用程序奠基坚实的基础。借助此功能,咱们已经完成了应用的MVP(最低可行产品)。在下一章中,咱们将应用程序部署到Web服务器上。

若是有理解不到位的地方,欢迎你们纠错。若是以为还能够,麻烦你点赞收藏或者分享一下,但愿能够帮到更多人。

相关文章
相关标签/搜索