翻译 |《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 title
document.title = 'New Note — Notedly';
});
return <div>New note</div>;
};
export default NewNote;
接下来,让咱们在 src/pages/index.js
文件中设置新路由:
// 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
服务器上。
如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦你点赞珍藏或者分享一下,心愿能够帮到更多人。