Browse Source

Redux-Toolkit

redux
YuJian920 3 years ago
parent
commit
5bee92de79
  1. 2
      package.json
  2. 21
      src/components/ProjectModal.tsx
  3. 11
      src/components/ProjectPopover.tsx
  4. 9
      src/context/auth-context.tsx
  5. 10
      src/context/index.tsx
  6. 6
      src/pages/Home/index.tsx
  7. 13
      src/pages/ProjectList/List/index.tsx
  8. 7
      src/pages/ProjectList/index.tsx
  9. 36
      src/store/Auth/index.ts
  10. 27
      src/store/ProjectList/index.ts
  11. 15
      src/store/index.tsx
  12. 5
      src/type/index.ts
  13. 65
      yarn.lock

2
package.json

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
"@craco/craco": "^6.4.3",
"@emotion/react": "^11.8.2",
"@emotion/styled": "^11.8.1",
"@reduxjs/toolkit": "^1.8.0",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
@ -21,6 +22,7 @@ @@ -21,6 +22,7 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-query": "^3.34.16",
"react-redux": "^7.2.6",
"react-router": "6",
"react-router-dom": "6",
"react-scripts": "5.0.0",

21
src/components/ProjectModal.tsx

@ -1,18 +1,25 @@ @@ -1,18 +1,25 @@
import React from "react";
import { Button, Drawer } from "antd";
import { useDispatch, useSelector } from "react-redux";
import {
projectListActions,
selectProjectModalOpen,
} from "../store/ProjectList";
const ProjectModal = () => {
const dispatch = useDispatch();
const projectModalOpen = useSelector(selectProjectModalOpen);
const ProjectModal = (props: {
projectModalOpen: boolean;
onClose: () => void;
}) => {
return (
<Drawer
onClose={props.onClose}
visible={props.projectModalOpen}
onClose={() => dispatch(projectListActions.closeProjectModal())}
visible={projectModalOpen}
width="100%"
>
<h1>Project Modal</h1>
<Button onClick={props.onClose}></Button>
<Button onClick={() => dispatch(projectListActions.closeProjectModal())}>
</Button>
</Drawer>
);
};

11
src/components/ProjectPopover.tsx

@ -1,9 +1,12 @@ @@ -1,9 +1,12 @@
import React from "react";
import { Button, Divider, List, Popover, Typography } from "antd";
import { useProjects } from "../hook/useProjects";
import styled from "@emotion/styled";
import { useDispatch } from "react-redux";
import { useProjects } from "../hook/useProjects";
import { projectListActions } from "../store/ProjectList";
const ProjectPopover = () => {
const dispatch = useDispatch();
const { data: projects, isLoading } = useProjects();
const pinnedProjects = projects?.filter((projects) => projects.pin);
@ -18,7 +21,11 @@ const ProjectPopover = () => { @@ -18,7 +21,11 @@ const ProjectPopover = () => {
))}
</List>
<Divider />
<Button style={{ padding: 0 }} type="link">
<Button
style={{ padding: 0 }}
type="link"
onClick={() => dispatch(projectListActions.openProjectModal())}
>
</Button>
</ContentContainer>

9
src/context/auth-context.tsx

@ -2,15 +2,10 @@ import React, { useState, useContext, ReactNode } from "react"; @@ -2,15 +2,10 @@ import React, { useState, useContext, ReactNode } from "react";
import { FullPageLoading } from "../components/lib";
import useAsync from "../hook/useAsync";
import useMount from "../hook/useMount";
import { User } from "../type";
import { AuthForm, User } from "../type";
import { request } from "../utils/request";
import * as auth from "../utils/token";
interface AuthForm {
username: string;
password: string;
}
interface AuthContext {
user: User | null;
login: (form: AuthForm) => Promise<void>;
@ -21,7 +16,7 @@ interface AuthContext { @@ -21,7 +16,7 @@ interface AuthContext {
const AuthContext = React.createContext<AuthContext | undefined>(undefined);
AuthContext.displayName = "AuthContext";
const bootstrapUser = async () => {
export const bootstrapUser = async () => {
let user = null;
const token = auth.getToken();
if (token) {

10
src/context/index.tsx

@ -1,12 +1,16 @@ @@ -1,12 +1,16 @@
import React, { ReactNode } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import { Provider } from "react-redux";
import { store } from "../store";
import { AuthProvider } from "./auth-context";
const AppProviders = ({ children }: { children: ReactNode }) => {
return (
<QueryClientProvider client={new QueryClient()}>
<AuthProvider>{children}</AuthProvider>
</QueryClientProvider>
<Provider store={store}>
<QueryClientProvider client={new QueryClient()}>
<AuthProvider>{children}</AuthProvider>
</QueryClientProvider>
</Provider>
);
};

6
src/pages/Home/index.tsx

@ -12,7 +12,6 @@ import ProjectModal from "../../components/ProjectModal"; @@ -12,7 +12,6 @@ import ProjectModal from "../../components/ProjectModal";
import ProjectPopover from "../../components/ProjectPopover";
const Home = () => {
const [projectModalOpen, setProjectModalOpen] = useState(false);
return (
<Container>
<PageHeader />
@ -25,10 +24,7 @@ const Home = () => { @@ -25,10 +24,7 @@ const Home = () => {
</Routes>
</BrowserRouter>
</Main>
<ProjectModal
projectModalOpen={projectModalOpen}
onClose={() => setProjectModalOpen(false)}
/>
<ProjectModal />
</Container>
);
};

13
src/pages/ProjectList/List/index.tsx

@ -5,11 +5,14 @@ import dayjs from "dayjs"; @@ -5,11 +5,14 @@ import dayjs from "dayjs";
import { Project, User } from "../../../type";
import Pin from "../../../components/Pin";
import { useEditProject } from "../../../hook/useProjects";
import { useDispatch } from "react-redux";
import { projectListActions } from "../../../store/ProjectList";
interface ListProps extends TableProps<Project> {
users: User[];
}
const ProjectList = ({ users, ...props }: ListProps) => {
const dispatch = useDispatch();
const { mutate } = useEditProject();
const pinProject = (id: number) => (pin: boolean) => mutate({ id, pin });
return (
@ -68,7 +71,15 @@ const ProjectList = ({ users, ...props }: ListProps) => { @@ -68,7 +71,15 @@ const ProjectList = ({ users, ...props }: ListProps) => {
overlay={
<Menu>
<Menu.Item key="edit">
<Button type="link" style={{ padding: 0 }}></Button>
<Button
onClick={() =>
dispatch(projectListActions.openProjectModal())
}
type="link"
style={{ padding: 0 }}
>
</Button>
</Menu.Item>
</Menu>
}

7
src/pages/ProjectList/index.tsx

@ -9,8 +9,11 @@ import useUsers from "../../hook/useUsers"; @@ -9,8 +9,11 @@ import useUsers from "../../hook/useUsers";
import useDocumentTitle from "../../hook/useDocumentTitle";
import useProjectsSearchParams from "../../hook/useProjectsSearchParams";
import { Row } from "../../components/lib";
import { useDispatch } from "react-redux";
import { projectListActions } from "../../store/ProjectList";
const ProjectList = () => {
const dispatch = useDispatch();
useDocumentTitle("项目列表", false);
const [params, setParam] = useProjectsSearchParams();
@ -25,7 +28,9 @@ const ProjectList = () => { @@ -25,7 +28,9 @@ const ProjectList = () => {
<Container>
<Row between={true}>
<h1></h1>
<Button></Button>
<Button onClick={() => dispatch(projectListActions.openProjectModal())}>
</Button>
</Row>
<SearchPanel users={users || []} param={params} setParam={setParam} />
{error ? (

36
src/store/Auth/index.ts

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
import { createSlice } from "@reduxjs/toolkit";
import { AppDispatch, RootState } from "..";
import * as auth from "../../utils/token";
import { AuthForm, User } from "../../type";
import { bootstrapUser } from "../../context/auth-context";
interface State {
user: User | null;
}
const initialState: State = {
user: null,
};
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
setUser(state, action) {
state.user = action.payload;
},
},
});
const { setUser } = authSlice.actions;
export const selectUser = (state: RootState) => state.auth.user;
export const login = (form: AuthForm) => (dispatch: AppDispatch) =>
auth.login(form).then((user) => dispatch(setUser(user)));
export const register = (form: AuthForm) => (dispatch: AppDispatch) =>
auth.register(form).then((user) => dispatch(setUser(user)));
export const logout = () => (dispatch: AppDispatch) =>
auth.logout().then(() => dispatch(setUser(null)));
export const bootstrap = () => (dispatch: AppDispatch) =>
bootstrapUser().then((user) => dispatch(setUser(user)));

27
src/store/ProjectList/index.ts

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "..";
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;

15
src/store/index.tsx

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
import { configureStore } from "@reduxjs/toolkit";
import { authSlice } from "./Auth";
import { projectListSlice } from "./ProjectList";
export const rootReducer = {
projectList: projectListSlice.reducer,
auth: authSlice.reducer,
};
export const store = configureStore({
reducer: rootReducer,
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;

5
src/type/index.ts

@ -15,3 +15,8 @@ export interface Project { @@ -15,3 +15,8 @@ export interface Project {
organization: string;
created: number;
}
export interface AuthForm {
username: string;
password: string;
}

65
yarn.lock

@ -1058,7 +1058,7 @@ @@ -1058,7 +1058,7 @@
core-js-pure "^3.20.2"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.17.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2"
integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==
@ -1570,6 +1570,16 @@ @@ -1570,6 +1570,16 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
"@reduxjs/toolkit@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.8.0.tgz#8ae875e481ed97e4a691aafa034f876bfd0413c4"
integrity sha512-cdfHWfcvLyhBUDicoFwG1u32JqvwKDxLxDd7zSmSoFw/RhYLOygIRtmaMjPRUUHmVmmAGAvquLLsKKU/677kSQ==
dependencies:
immer "^9.0.7"
redux "^4.1.2"
redux-thunk "^2.4.1"
reselect "^4.1.5"
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@ -1947,6 +1957,14 @@ @@ -1947,6 +1957,14 @@
dependencies:
"@types/node" "*"
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@ -2048,6 +2066,16 @@ @@ -2048,6 +2066,16 @@
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.20":
version "7.1.23"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.23.tgz#3c2bb1bcc698ae69d70735f33c5a8e95f41ac528"
integrity sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==
dependencies:
"@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react@*", "@types/react@^17.0.41":
version "17.0.41"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.41.tgz#6e179590d276394de1e357b3f89d05d7d3da8b85"
@ -4883,7 +4911,7 @@ history@^5.2.0: @@ -4883,7 +4911,7 @@ history@^5.2.0:
dependencies:
"@babel/runtime" "^7.7.6"
hoist-non-react-statics@^3.3.1:
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -7481,7 +7509,7 @@ prompts@^2.0.1, prompts@^2.4.2: @@ -7481,7 +7509,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.8.1:
prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -8000,7 +8028,7 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0: @@ -8000,7 +8028,7 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.1:
react-is@^17.0.1, react-is@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
@ -8028,6 +8056,18 @@ react-query@^3.34.16, react-query@^3.5.10: @@ -8028,6 +8056,18 @@ react-query@^3.34.16, react-query@^3.5.10:
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-redux@^7.2.6:
version "7.2.6"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa"
integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/react-redux" "^7.1.20"
hoist-non-react-statics "^3.3.2"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^17.0.2"
react-refresh@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
@ -8155,6 +8195,18 @@ redent@^3.0.0: @@ -8155,6 +8195,18 @@ redent@^3.0.0:
indent-string "^4.0.0"
strip-indent "^3.0.0"
redux-thunk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==
redux@^4.0.0, redux@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"
integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==
dependencies:
"@babel/runtime" "^7.9.2"
regenerate-unicode-properties@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
@ -8257,6 +8309,11 @@ requires-port@^1.0.0: @@ -8257,6 +8309,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
reselect@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6"
integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==
resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"

Loading…
Cancel
Save