翻譯 | 《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
服務器上。
若是有理解不到位的地方,歡迎你們糾錯。若是以爲還能夠,麻煩你點贊收藏或者分享一下,但願能夠幫到更多人。