記事ディレクトリ
-
- 1. プロジェクトの起動: プロジェクトの初期化と構成
- 2. React と Hook アプリケーション: プロジェクト リストを実装します。
- 3. TSアプリ:JSゴッドアシスト・強力タイプ
- 4. JWT、ユーザー認証、非同期リクエスト
- 5. CSS は実際には非常にシンプルです - CSS-in-JS を使用してスタイルを追加します
- 6. ユーザー エクスペリエンスの最適化 - 読み込みとエラー状態の処理
- 7. フック、ルーティング、および URL 状態の管理
- 8. ユーザーセレクターと項目編集機能
- 9. 詳細な React 状態管理と Redux メカニズム
- 10. 反応クエリを使用してデータを取得し、キャッシュを管理する
学習コンテンツのソース: React + React Hook + TS ベスト プラクティス - MOOC
元のチュートリアルと比較して、学習の開始時に最新バージョン (2023.03) を使用しました。
アイテム | バージョン |
---|---|
反応&反応ダム | ^18.2.0 |
反応ルーターと反応ルーターダム | ^6.11.2 |
と | ^4.24.8 |
@commitlint/cli および @commitlint/config-conventional | ^17.4.4 |
eslint-config-prettier | ^8.6.0 |
ハスキー | ^8.0.3 |
糸くずステージ | ^13.1.2 |
より美しい | 2.8.4 |
jsonサーバー | 0.17.2 |
クラコレス | ^2.0.0 |
@クラコ/クラコ | ^7.1.0 |
qs | ^6.11.0 |
デイジェス | ^1.11.7 |
反応ヘルメット | ^6.1.0 |
@types/react-helmet | ^6.1.6 |
反応クエリ | ^6.1.0 |
@welldone-software/why-did-you-render | ^7.0.1 |
@emotion/反応 & @emotion/styled | ^11.10.6 |
具体的な構成や動作、内容は異なりますし、「落とし穴」も異なります。。。
1. プロジェクトの起動: プロジェクトの初期化と構成
2. React と Hook アプリケーション: プロジェクト リストを実装します。
3. TSアプリ:JSゴッドアシスト・強力タイプ
4. JWT、ユーザー認証、非同期リクエスト
5. CSS は実際には非常にシンプルです - CSS-in-JS を使用してスタイルを追加します
6. ユーザー エクスペリエンスの最適化 - 読み込みとエラー状態の処理
7. フック、ルーティング、および URL 状態の管理
8. ユーザーセレクターと項目編集機能
9. 詳細な React 状態管理と Redux メカニズム
10. 反応クエリを使用してデータを取得し、キャッシュを管理する
コピーをブラッシングした後、メインブランチに戻ります
1. url パラメータを使用してプロジェクトのモーダル ボックスの状態を管理します
url
プロジェクトのモーダルボックスの状態をパラメータで管理するには、src\screens\ProjectList\utils.ts
新しいモーダルボックスを追加します。Custom Hook
export const useProjectModal = () => {
const [{
projectCreate}, setProjectCreate] = useUrlQueryParam(['projectCreate'])
const open = () => setProjectCreate({
projectCreate: true})
const close = () => setProjectCreate({
projectCreate: false}) // 若在url显示 false,可以改为 undefined 隐藏
return {
projectModalOpen: projectCreate === 'true',
open,
close
}
}
次に、前の組み合わせコンポーネントの使用を削除し、対応する位置でコンポーネントを直接使用し、useProjectModal
状態とメソッドを呼び出します。
編集src\authenticated-app.tsx
:
...
export const AuthenticatedApp = () => {
- const [isOpen, setIsOpen] = useState(false);
useDocumentTitle("项目列表", false);
return (
<Container>
- <PageHeader
- projectButton={
- <ButtonNoPadding type="link" onClick={() => setIsOpen(true)}>
- 创建项目
- </ButtonNoPadding>
- }
- />
+ <PageHeader/>
<Main>
<Router>
<Routes>
- <Route
- path="/projects"
- element={
- <ProjectList
- projectButton={
- <ButtonNoPadding
- type="link"
- onClick={() => setIsOpen(true)}
- >
- 创建项目
- </ButtonNoPadding>
- }
- />
- }
- />
+ <Route path="/projects" element={<ProjectList/>}/>
...
</Routes>
</Router>
</Main>
- <ProjectModal isOpen={isOpen} onClose={() => setIsOpen(false)} />
+ <ProjectModal/>
</Container>
);
};
- const PageHeader = (props: { projectButton: JSX.Element }) => {
+ const PageHeader = () => {
...
return (
<Header between={true}>
<HeaderLeft gap={true}>
...
- <ProjectPopover {...props} />
+ <ProjectPopover/>
<span>用户</span>
</HeaderLeft>
...
</Header>
);
};
...
編集src\screens\ProjectList\index.tsx
:
...
- import { useProjectsSearchParams } from "./utils";
+ import { useProjectModal, useProjectsSearchParams } from "./utils";
...
+ import { ButtonNoPadding } from "components/lib";
- export const ProjectList = ({ projectButton }: { projectButton: JSX.Element }) => {
+ export const ProjectList = () => {
+ const {open} = useProjectModal()
...
return (
<Container>
<Row justify="space-between">
<h1>项目列表</h1>
- {projectButton}
+ <ButtonNoPadding type="link" onClick={open}>创建项目</ButtonNoPadding>
</Row>
<SearchPanel users={users || []} param={param} setParam={setParam} />
{error ? (
<Typography.Text type="danger">{error.message}</Typography.Text>
) : null}
<List
- projectButton={projectButton}
...
/>
</Container>
);
};
...
編集src\screens\ProjectList\components\List.tsx
:
...
+ import { useProjectModal } from "../utils";
...
interface ListProps extends TableProps<Project> {
users: User[];
refresh?: () => void;
- projectButton: JSX.Element;
}
// type PropsType = Omit<ListProps, 'users'>
export const List = ({ users, ...props }: ListProps) => {
+ const {open} = useProjectModal()
...
return (
<Table
pagination={false}
columns={[
...
{
render: (text, project) => {
const items: MenuProps["items"] = [
{
key: "edit",
label: "编辑",
+ onClick: open
},
];
return (
- <Dropdown dropdownRender={() => props.projectButton}>
+ <Dropdown menu={
{items}}>
<ButtonNoPadding
type="link"
onClick={(e) => e.preventDefault()}
>
...
</ButtonNoPadding>
</Dropdown>
);
},
},
]}
{...props}
></Table>
);
};
編集src\screens\ProjectList\components\ProjectModal.tsx
:
...
+ import { useProjectModal } from "../utils";
- export const ProjectModal = ({isOpen, onClose}: {isOpen: boolean; onClose: () => void;}) => {
+ export const ProjectModal = () => {
+ const {projectModalOpen, close} = useProjectModal()
return (
- <Drawer onClose={onClose} open={isOpen} width="100%">
+ <Drawer onClose={close} open={projectModalOpen} width="100%">
<h1>Project Modal</h1>
- <Button onClick={onClose}>关闭</Button>
+ <Button onClick={close}>关闭</Button>
</Drawer>
);
};
編集src\screens\ProjectList\components\ProjectPopover.tsx
:
...
+ import { ButtonNoPadding } from "components/lib";
+ import { useProjectModal } from "../utils";
- export const ProjectPopover = ({projectButton}: {projectButton: JSX.Element;}) => {
+ export const ProjectPopover = () => {
+ const {open} = useProjectModal()
...
const content = (
<ContentContainer>
...
- {projectButton}
+ <ButtonNoPadding type="link" onClick={open}>创建项目</ButtonNoPadding>
</ContentContainer>
);
...
};
...
変更後のページの効果を確認し、エラーを報告します。
useLocation() may be used only in the context of a <Router> component.
コードを見ると、 と が でラップされていないことがわかりますsrc\authenticated-app.tsx
がPageHeader
、ProjectModal
使用Router
するuseUrlQueryParam
関連関数をRouter
に配置する必要があるため、それを変更します。
...
export const AuthenticatedApp = () => {
...
return (
<Container>
+ <Router>
<PageHeader/>
<Main>
- <Router>
<Routes>
<Route path="/projects" element={<ProjectList/>}/>
<Route path="/projects/:projectId/*" element={<ProjectDetail />} />
<Route index element={<Navigate to="projects" />} />
</Routes>
- </Router>
</Main>
<ProjectModal/>
+ </Router>
</Container>
);
};
...
ページをもう一度確認してください。問題なく動作しています。
2. サーバー側のキャッシュを処理するには、react-query を使用します
3. ガードと入力し、useQuery を使用してプロジェクト リストをキャッシュします
一部のビデオはオンラインで視聴できます: https://space.bilibili.com/2814498/video
react-query
別のバージョンをインストールする前にjira-dev-tool
インストールされています。インストール コマンドは次のとおりです。
npm i react-query # --force
次に、react-query
RetrofitProject
関連のインターフェイス呼び出しを使用します。
編集src\utils\project.ts
...
import {
useQuery } from "react-query";
export const useProjects = (param?: Partial<Project>) => {
const client = useHttp();
return useQuery<Project[]>(['projects', param], () => client("projects", {
data: cleanObject(param || {
}) }))
};
...
useQuery
キャッシュがヒットした場合、onSuccess
対応するメソッドはトリガーされません- 以前は、この部分の関数は
react-query
を模倣して記述されていました。一部の関数は置換後に直接使用できますが、一部の関数は使用できません。
- 前の
rerun
(ビデオ内のretry
) 関数は、最初のパラメーターを配列形式useQuery
に変更できます。配列の最初の項目は伝統的な意味であり、後続のパラメーターは暗黙的な " " をトリガーする "依存"として使用できます。パラメータが変更されると、それがヒットしますキャッシュは依然として対応するメソッドをトリガーしますQueryKey
[key, ...otherParams]
key
rerun
onSuccess
- リクエストでエラーが報告される場合、
useQuery
同じError
データが で返されるが、型 (unknown
ではないError
) が必要なものではなく、その後の使用で型エラーが報告される場合、解決策は 2 つあります。
- 1. 宣言された戻り値の型を表示します。
useQuery<Project[], Error>()
- 2. タイプガードを使用します。詳細については以下を参照してください。
編集src\components\lib.tsx
タイプガードとErrorBox
コンポーネントを追加しました:
...
// 类型守卫
const isError = (value: any): value is Error => value?.message
export const ErrorBox = ({
error}: {
error: unknown}) => {
if (isError(error)) {
return <Typography.Text type="danger">{
error.message}</Typography.Text>
}
return null
}
...
is
ts
コンパイラが変数の型を絞り込むのに役立つ型述語です。
ErrorBox
使用する必要がある他の場所は順番に置き換えられます。
編集src\unauthenticated-app\index.tsx
:
...
import {
ErrorBox } from "components/lib";
export const UnauthenticatedApp = () => {
...
return (
<Container>
...
<ShadowCard>
<Title>{
isRegister ? "请注册" : "请登录"}</Title>
<ErrorBox error={
error}/>
...
</ShadowCard>
</Container>
);
};
...
編集src\screens\ProjectList\index.tsx
:
...
- import { ButtonNoPadding } from "components/lib";
+ import { ButtonNoPadding, ErrorBox } from "components/lib";
export const ProjectList = () => {
...
const {
isLoading,
error,
data: list,
- rerun
} = useProjects(useDebounce(param));
const { data: users } = useUsers();
return (
<Container>
...
<SearchPanel users={users || []} param={param} setParam={setParam} />
- {error ? (
- <Typography.Text type="danger">{error.message}</Typography.Text>
- ) : null}
+ <ErrorBox error={error}/>
<List
- refresh={rerun}
loading={isLoading}
users={users || []}
dataSource={list || []}
/>
</Container>
);
};
...
rerun
(ビデオ内retry
)を使用している部分も削除します。
編集src\screens\ProjectList\components\List.tsx
:
...
export const List = ({ users, ...props }: ListProps) => {
...
const starProject = (id: number) => (star: boolean) =>
- mutate({ id, star }).then(props.refresh);
+ mutate({ id, star })
return (
<Table
pagination={false}
columns={[
...
{
render: (text, project) => {
const items: MenuProps["items"] = [
{
key: "edit",
label: "编辑",
onClick: open,
- },
+ }, {
+ key: "delete",
+ label: "删除",
+ // onClick: open,
+ },
];
...
},
},
]}
{...props}
></Table>
);
};
ページ効果、クエリ機能は正常、エラーメッセージ表示は正常であることを確認してください。
一部の参考ノートはまだ草案段階にあるため、今後の内容に注目してください。。。