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

写在最后面

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

为了进步大家的浏览体验,对语句的构造和内容略有调整。如果发现本文中有存在瑕疵的中央,或者你有任何意见或者倡议,能够在评论区留言,或者加我的微信:code_maomao,欢送互相沟通交流学习。

(゚∀゚)..:*☆哎哟不错哦

第16章 创立,读取,更新和删除操作

我喜爱纸质笔记本,简直始终都随身携带着。通常,它们绝对便宜,我很快就写满了长期的想法。不久前,我购买了价格更高的精装本笔记本,下面有精美的封面和精美的纸本。购买时,我对笔记本中将要呈现的草图和打算抱有很大的野心,然而它在我的办公桌上呆了几个月。最终,我将其放在架子上,回到了我的规范笔记本电脑品牌。

就像我的笔记本一样,咱们的利用仅在用户可能应用时才有用。你可能会从咱们的API开发中回想起Notedly应用程序是一个“ CRUD”(创立,读取,更新和删除)应用程序。通过身份验证的用户能够创立新笔记、浏览笔记、更新笔记的内容或笔记作为收藏夹的状态以及删除笔记。在本章中,咱们将在Web用户界面中实现所有这些性能。为了实现这些工作,咱们将编写GraphQL申请和查问。

创立新笔记

以后,咱们能够查看笔记,但无奈创立笔记。这相似于没有笔的笔记本。让咱们为用户增加创立新笔记的性能。咱们将通过创立一个用户能够在其中写笔记的textarea模式。

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

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

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

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

// import the NewNote route componentimport 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 formconst [value, setValue] = useState({ content: props.content || '' });// update the state when a user types in the formconst 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 componentimport NoteForm from '../components/NoteForm';const NewNote = props => {useEffect(() => {// update the document titledocument.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 queryconst NEW_NOTE = gql`mutation newNote($content: String!) {newNote(content: $content) {idcontentcreatedAtfavoriteCountfavoritedBy {idusername}author {usernameid}}}`;const NewNote = props => {useEffect(() => {// update the document titledocument.title = 'New Note — Notedly';});const [data, { loading, error }] = useMutation(NEW_NOTE, {onCompleted: data => {// when complete, redirect the user to the note pageprops.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) {cursorhasNextPagenotes {idcreatedAtcontentfavoriteCountauthor {usernameidavatar}}}}`;const GET_NOTE = gql`query note($id: ID!) {note(id: $id) {idcreatedAtcontentfavoriteCountauthor {usernameidavatar}}}`;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 queryimport { GET_NOTES } from '../gql/query';// within the NewNote component update the mutation//everything else stays the sameconst NewNote = props => {useEffect(() => {// update the document titledocument.title = 'New Note — Notedly';});const [data, { loading, error }] = useMutation(NEW_NOTE, {// refetch the GET_NOTES query to update the cacherefetchQueries: [{ query: GET_NOTES }],onCompleted: data => {// when complete, redirect the user to the note pageprops.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 queryconst GET_MY_NOTES = gql`query me {me {idusernamenotes {idcreatedAtcontentfavoriteCountauthor {usernameidavatar}}}}`;// update to include GET_MY_NOTESexport { 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 titledocument.title = 'My Notes — Notedly';});const { loading, error, data } = useQuery(GET_MY_NOTES);// if the data is loading, our app will display a loading messageif (loading) return 'Loading...';// if there is an error fetching the data, display an error messageif (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 messageif (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 queryconst GET_MY_FAVORITES = gql`query me {me {idusernamefavorites {idcreatedAtcontentfavoriteCountauthor {usernameidavatar}}}}`;// update to include GET_MY_FAVORITESexport { 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 queryimport { GET_MY_FAVORITES } from '../gql/query';const Favorites = () => {useEffect(() => {// update the document titledocument.title = 'Favorites — Notedly';});const { loading, error, data } = useQuery(GET_MY_FAVORITES);// if the data is loading, our app will display a loading messageif (loading) return 'Loading...';// if there is an error fetching the data, display an error messageif (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 messageif (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 cacherefetchQueries: [{ query: GET_MY_NOTES }, { query: GET_NOTES }],onCompleted: data => {// when complete, redirect the user to the note pageprops.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 componentimport Note from '../components/Note';// import the GET_NOTE queryimport { GET_NOTE } from '../gql/query';const EditNote = props => {// store the id found in the url as a variableconst id = props.match.params.id;// define our note queryconst { loading, error, data } = useQuery(GET_NOTE, { variables: { id } });// if the data is loading, display a loading messageif (loading) return 'Loading...';// if there is an error fetching the data, display an error messageif (error) return <p>Error! Note not found</p>;// if successful, pass the data to the note componentreturn <Note note={data.note} />;};export default EditNote;

当初,咱们能够通过将页面增加到src/pages/index.js中的路由来使页面可切换:

// import the edit page componentimport 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 componentimport NoteForm from '../components/NoteForm';

当初,咱们能够更新EditNote组件来应用咱们的编辑表单。咱们能够应用content属性将笔记的内容传递给表单组件。尽管咱们的GraphQL批改仅承受原作者的更新,但咱们也能够只将表单显示的范畴限度为笔记的作者,以防止混同其余用户。

首先,将新查问增加到src/gql/query.js文件中,以获取以后用户,其用户ID以及珍藏的笔记ID的列表:

// add GET_ME to our queriesconst GET_ME = gql`query me {me {idfavorites {id}}}`;// update to include GET_MEexport {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 componentimport 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 variableconst id = props.match.params.id;// define our note queryconst { loading, error, data } = useQuery(GET_NOTE, { variables: { id } });// fetch the current user's dataconst { data: userdata } = useQuery(GET_ME);// if the data is loading, display a loading messageif (loading) return 'Loading...';// if there is an error fetching the data, display an error messageif (error) return <p>Error! Note not found</p>;// if the current user and the author of the note do not matchif (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 componentreturn <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) {idcontentcreatedAtfavoriteCountfavoritedBy {idusername}author {usernameid}}}`;export { EDIT_NOTE };

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

// import the mutationimport { EDIT_NOTE } from '../gql/mutation';const EditNote = props => {// store the id found in the url as a variableconst id = props.match.params.id;// define our note queryconst { loading, error, data } = useQuery(GET_NOTE, { variables: { id } });// fetch the current user's dataconst { data: userdata } = useQuery(GET_ME);// define our mutationconst [editNote] = useMutation(EDIT_NOTE, {variables: {id},onCompleted: () => {props.history.push(`/note/${id}`);}});// if the data is loading, display a loading messageif (loading) return 'Loading...';// if there is an error fetching the data, display an error messageif (error) return <p>Error!</p>;// if the current user and the author of the note do not matchif (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 componentreturn <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 componentsimport NoteUser from './NoteUser';// import the IS_LOGGED_IN local queryimport { 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 messageif (loading) return <p>Loading...</p>;// if there is an error fetching the data, display an error messageif (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 queryimport { GET_ME } from '../gql/query'; const NoteUser = props => { const { loading, error, data } = useQuery(GET_ME); // if the data is loading, display a loading messageif (loading) return <p>Loading...</p>; // if there is an error fetching the data, display an error messageif (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_NOTEexport { 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 mutationimport { DELETE_NOTE } from '../gql/mutation';// import queries to refetch after note deletionimport { 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 cacherefetchQueries: [{ query: GET_MY_NOTES, GET_NOTES }],onCompleted: data => {// redirect the user to the "my notes" pageprops.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 componentimport DeleteNote from './DeleteNote';const NoteUser = props => {const { loading, error, data } = useQuery(GET_ME);// if the data is loading, display a loading messageif (loading) return <p>Loading...</p>;// if there is an error fetching the data, display an error messageif (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 stateconst [count, setCount] = useState(props.favoriteCount);// store if the user has favorited the note as stateconst [favorited, setFavorited] = useState(// check if the note exists in the user favorites listprops.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 mutationconst TOGGLE_FAVORITE = gql`mutation toggleFavorite($id: ID!) {toggleFavorite(id: $id) {idfavoriteCount}}`;// update to include TOGGLE_FAVORITEexport { 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 mutationimport { TOGGLE_FAVORITE } from '../gql/mutation';// add the GET_MY_FAVORITES query to refetchimport { GET_MY_FAVORITES } from '../gql/query';const FavoriteNote = props => {// store the note's favorite count as stateconst [count, setCount] = useState(props.favoriteCount);// store if the user has favorited the note as stateconst [favorited, setFavorited] = useState(// check if the note exists in the user favorites listprops.me.favorites.filter(note => note.id === props.noteId).length > 0);// toggleFavorite mutation hookconst [toggleFavorite] = useMutation(TOGGLE_FAVORITE, {variables: {id: props.noteId},// refetch the GET_MY_FAVORITES query to update the cacherefetchQueries: [{ 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 favoritereturn (<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服务器上。

如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦你点赞珍藏或者分享一下,心愿能够帮到更多人。