記事ディレクトリ
学習コンテンツのソース: 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 メカニズム
1&2
3&4
5~8
9. redux-toolkitの設定
redux-toolkit
これはredux
の 2 番目のパッケージであり、主に次の 3 つの主要な問題点を解決します。
- 複雑な構成
- 追加するパッケージが多すぎます
- 必要な定型コードが多すぎる
プロジェクトは最終的には使用されないためredux
、学習と開発のために新しいブランチが開かれ、ブランチが作成されますredux-toolkit
インストールの依存関係:
npm i react-redux @reduxjs/toolkit # --force
新しいsrc\store\index.tsx
:
import {
configureStore } from "@reduxjs/toolkit"
export const rootReducer = {
}
export const store = configureStore({
reducer: rootReducer
})
export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
新しいsrc\screens\ProjectList\projectList.slice.ts
:
import {
createSlice } from "@reduxjs/toolkit";
interface State {
projectModalOpen: boolean;
}
const initialState: State = {
projectModalOpen: false
}
export const projectListSlice = createSlice({
name: 'projectListSlice',
initialState,
reducers: {
openProjectModal(state, action) {
},
closeProjectModal(state, action) {
}
}
})
state
Q:の属性に値を直接割り当てることができるのはなぜですか?
回答:redux
組み込みを使用してimmer
不変データに処理し、「シャドウ状態」を作成し、最後に元の状態全体を置き換えます。
10.redux-toolkit管理モーダルボックスを適用します。
完璧src\screens\ProjectList\projectList.slice.ts
:
import {
createSlice } from "@reduxjs/toolkit";
import {
RootState } from "store";
interface State {
projectModalOpen: boolean;
}
const initialState: State = {
projectModalOpen: false,
};
export const projectListSlice = createSlice({
name: "projectListSlice",
initialState,
reducers: {
openProjectModal(state) {
state.projectModalOpen = true
},
closeProjectModal(state) {
state.projectModalOpen = false
},
},
});
export const projectListActions = projectListSlice.actions;
export const selectProjectModalOpen = (state: RootState) => state.projectList.projectModalOpen
その後の使用:
- 導入
import { useDispatch, useSelector } from "react-redux";
import { projectListActions, selectProjectModalOpen } from "../projectList.slice";
hook
取得するために使用しますdispatch
:const dispatch = useDispatch()
- 次の呼び出しを使用して
dispatch
モーダルを開きます。() => dispatch(projectListActions.openProjectModal())
- 呼び出しを使用して
dispatch
モーダルを閉じます。() => dispatch(projectListActions.closeProjectModal())
hook
モーダル ボックスの現在の開閉状態を取得するために使用します。useSelector(selectProjectModalOpen)
useSelector
ルート状態ツリーを読み取るために使用されます
変更src\store\index.tsx
(導入projectListSlice
):
import {
configureStore } from "@reduxjs/toolkit";
import {
projectListSlice } from "screens/ProjectList/projectList.slice";
export const rootReducer = {
projectList: projectListSlice.reducer,
};
export const store = configureStore({
reducer: rootReducer,
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
ReturnType
関数の戻り値を読み取るために使用される型
以前AuthenticatedApp
(サブカプセル化モーダルボックスコンポーネントを導入する場所)は、状態量(const [isOpen, setIsOpen] = useState(false)
)をレイヤーごとに作成し、モーダルボックスを使用する場所に渡していました。
redux
今のところはこれですべてです。ofを使用してdispatch
モーダルの状態を変更してみましょう。
編集src\authenticated-app.tsx
:
...
export const AuthenticatedApp = () => {
- const [isOpen, setIsOpen] = useState(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>
- }
- />
+ <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 { useDispatch } from "react-redux";
+ import { ButtonNoPadding } from "components/lib";
+ import { projectListActions } from "./projectList.slice";
- export const ProjectList = ({
- projectButton,
- }: {
- projectButton: JSX.Element;
- }) => {
+ export const ProjectList = () => {
...
+ const dispatch = useDispatch()
return (
<Container>
<Row justify="space-between">
<h1>项目列表</h1>
- {projectButton}
+ <ButtonNoPadding type="link" onClick={() => dispatch(projectListActions.openProjectModal())}>
+ 创建项目
+ </ButtonNoPadding>
</Row>
...
<List
- projectButton={projectButton}
...
/>
</Container>
);
};
...
編集src\screens\ProjectList\components\List.tsx
:
...
+ import { useDispatch } from "react-redux";
+ import { projectListActions } from "../projectList.slice";
interface ListProps extends TableProps<Project> {
users: User[];
refresh?: () => void;
- projectButton: JSX.Element;
}
// type PropsType = Omit<ListProps, 'users'>
export const List = ({ users, ...props }: ListProps) => {
+ const dispatch = useDispatch()
...
return (
<Table
pagination={false}
columns={[
...
{
render: (text, project) => {
+ const items: MenuProps["items"] = [
+ {
+ key: "edit",
+ label: "编辑",
+ onClick: () => dispatch(projectListActions.openProjectModal()),
+ },
+ ];
return (
- <Dropdown dropdownRender={() => props.projectButton}>
+ <Dropdown menu={
{items}}>
...
</Dropdown>
);
},
},
]}
{...props}
></Table>
);
};
編集src\screens\ProjectList\components\ProjectModal.tsx
:
...
+ import { useDispatch, useSelector } from "react-redux";
+ import { projectListActions, selectProjectModalOpen } from "../projectList.slice";
- export const ProjectModal = ({
- isOpen,
- onClose,
- }: {
- isOpen: boolean;
- onClose: () => void;
- }) => {
+ export const ProjectModal = () => {
+ const dispatch = useDispatch()
+ const projectModalOpen = useSelector(selectProjectModalOpen)
return (
- <Drawer onClose={onClose} open={isOpen} width="100%">
+ <Drawer
+ onClose={() => dispatch(projectListActions.closeProjectModal())}
+ open={projectModalOpen}
+ width="100%"
+ >
<h1>Project Modal</h1>
- <Button onClick={onClose}>关闭</Button>
+ <Button onClick={() => dispatch(projectListActions.closeProjectModal())}>关闭</Button>
</Drawer>
);
};
編集src\screens\ProjectList\components\ProjectPopover.tsx
:
...
+ import { useDispatch } from "react-redux";
+ import { projectListActions } from "../projectList.slice";
+ import { ButtonNoPadding } from "components/lib";
- export const ProjectPopover = ({
- projectButton,
- }: {
- projectButton: JSX.Element;
- }) => {
+ export const ProjectPopover = () => {
+ const dispatch = useDispatch()
...
const content = (
<ContentContainer>
<Typography.Text type="secondary">收藏项目</Typography.Text>
<List>
{starProjects?.map((project) => (
- <List.Item>
+ <List.Item key={project.id}>
<List.Item.Meta title={project.name} />
</List.Item>
))}
</List>
<Divider />
- {projectButton}
+ <ButtonNoPadding type="link" onClick={() => dispatch(projectListActions.openProjectModal())}>
+ 创建项目
+ </ButtonNoPadding>
</ContentContainer>
);
...
};
...
このページにアクセスすると、次のエラーが表示されます。
could not find react-redux context value; please ensure the component is wrapped in a <Provider>
これは、 of がグローバルにredux
バインドされていないためです。store
context
編集src\context\index.tsx
:
...
+ import { store } from "store";
+ import { Provider } from "react-redux";
export const AppProvider = ({ children }: { children: ReactNode }) => {
return (
+ <Provider store={store}>
<QueryClientProvider client={new QueryClient()}>
<AuthProvider>{children}</AuthProvider>
</QueryClientProvider>
+ </Provider>
);
};
もう一度ページにアクセスしてください。機能は正常です
一部の参考ノートはまだ草案段階にあるため、今後の内容に注目してください。。。