翻譯 | 《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服務器上。

若是有理解不到位的地方,歡迎你們糾錯。若是以爲還能夠,麻煩你點贊收藏或者分享一下,但願能夠幫到更多人。

相關文章
相關標籤/搜索