翻译 | 《JavaScript Everywhere》第16章 建立,读取,更新和删除操做php
你们好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。前端
为了提升你们的阅读体验,对语句的结构和内容略有调整。若是发现本文中有存在瑕疵的地方,或者你有任何意见或者建议,能够在评论区留言,或者加个人微信:code_maomao,欢迎相互沟通交流学习。react
(σ゚∀゚)σ..:*☆哎哟不错哦数据库
我喜欢纸质笔记本,几乎一直都随身携带着。一般,它们相对便宜,我很快就写满了临时的想法。不久前,我购买了价格更高的精装本笔记本,上面有精美的封面和精美的纸本。购买时,我对笔记本中将要出现的草图和计划抱有很大的野心,可是它在个人办公桌上呆了几个月。最终,我将其放在架子上,回到了个人标准笔记本电脑品牌。缓存
就像个人笔记本同样,咱们的应用仅在用户可以使用时才有用。你可能会从咱们的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
的缓存,可是更简单的方法是使用Apollo
的refetchQueries
功能可在执行修改时有意地更新缓存。
为此,咱们须要访问预先编写的查询。到目前为止,咱们一直将它们包含在组件文件的顶部,但让咱们将其移动到本身的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 Router
的withRouter
高阶组件:
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
服务器上。
若是有理解不到位的地方,欢迎你们纠错。若是以为还能够,麻烦你点赞收藏或者分享一下,但愿能够帮到更多人。