Selaa lähdekoodia

2023.2.2优化useContextSelector

xyh 2 vuotta sitten
vanhempi
commit
153d1d70c2

+ 1 - 0
packages/app/package.json

@@ -16,6 +16,7 @@
     "antd": "^5.1.7",
     "axios": "^1.2.6",
     "classnames": "^2.3.2",
+    "fast-deep-equal": "^3.1.3",
     "immer": "^9.0.19",
     "lottie-react": "^2.3.1",
     "react": "^18.2.0",

+ 1 - 0
packages/app/src/hooks/index.ts

@@ -1 +1,2 @@
 export * from './usePageContext';
+export * from './useContextSection';

+ 34 - 0
packages/app/src/hooks/useContextSection.ts

@@ -0,0 +1,34 @@
+import {deepEqual} from '@utils';
+import {useLatest} from 'ahooks';
+import {useMemo} from 'react';
+import {useContextSelector, Context} from 'use-context-selector';
+
+const selectorFn = <T, R>(state: T) => state ;
+
+export function useContextSection<T, R>(
+  context: Context<T>,
+  selector: (state: T) => R,
+) {
+  const f = useLatest(selector ?? selectorFn);
+
+  const callback = useMemo(
+    function() {
+      let memoState: R | null = null;
+
+      return function(state: T) {
+        const newState = f.current(state);
+
+        if (!memoState || !deepEqual(memoState, newState)) return (memoState = newState);
+
+        return memoState;
+      };
+    },
+    [f],
+  );
+
+  return useContextSelector(context, callback);
+}
+
+export function useContext<T>(context: Context<T>) {
+  return useContextSection(context, selectorFn);
+}

+ 3 - 3
packages/app/src/pages/department/filter/hooks.ts

@@ -1,7 +1,7 @@
 import {useState} from 'react';
-import {useContextSelector} from 'use-context-selector';
 import {context, pageContext} from '../context';
 import {exportDepartment} from '@apis';
+import {useContextSection} from '@hooks';
 
 export function useField() {
   const [state, setState] = useState({name: '', code: ''});
@@ -16,8 +16,8 @@ export function useField() {
 }
 
 export function useHandle(name: string, code: string) {
-  const dispatch = useContextSelector(context, state => state[1]);
-  const {page, pageSize} = useContextSelector(pageContext, state => state[0]);
+  const dispatch = useContextSection(context, state => state[1]);
+  const {page, pageSize} = useContextSection(pageContext, state => state[0]);
 
   function onSearch() {
     dispatch({type: 'SEARCH', payload: {name, code}});

+ 2 - 2
packages/app/src/pages/department/table/hooks.tsx

@@ -1,5 +1,4 @@
 import {useMutation, useQuery} from '@tanstack/react-query';
-import {useContext, useContextSelector} from 'use-context-selector';
 import {context, pageContext} from '../context';
 import {deleteDepartment, getDepartmentList} from '@apis';
 import {useState} from 'react';
@@ -7,11 +6,12 @@ import {ColumnsType} from 'antd/es/table';
 import {DepartmentListData} from '@models';
 import {useBoolean} from 'ahooks';
 import {Button, Modal, message} from 'antd';
+import {useContext, useContextSection} from '@hooks';
 
 export function useList() {
   const [count, setCount] = useState(0);
   const [{page, pageSize}, dispatch] = useContext(pageContext);
-  const {name, code} = useContextSelector(context, state => state[0]);
+  const {name, code} = useContextSection(context, state => state[0]);
   const {data, isFetching, refetch} = useQuery(
     [name, code, page, pageSize, getDepartmentList.name],
     async function() {

+ 2 - 2
packages/app/src/pages/menu/filter/hooks.ts

@@ -1,6 +1,6 @@
 import {useState} from 'react';
-import {useContextSelector} from 'use-context-selector';
 import {context} from '../context';
+import {useContextSection} from '@hooks';
 
 export function useField() {
   const [state, setState] = useState('');
@@ -13,7 +13,7 @@ export function useField() {
 }
 
 export function useHandle(value: string) {
-  const dispatch = useContextSelector(context, state => state[1]);
+  const dispatch = useContextSection(context, state => state[1]);
 
   function onSearch() {
     dispatch({type: 'SEARCH', payload: {name: value}});

+ 13 - 4
packages/app/src/pages/menu/index.tsx

@@ -1,9 +1,10 @@
 import Filter from './filter';
-import {FC, ReactNode} from 'react';
+import {FC, ReactNode, useEffect} from 'react';
 import {context, pageContext, useContextReducer} from './context';
 import {usePageContextReducer} from '@hooks';
 import TableList from './table';
-import {useLocation} from 'react-router-dom';
+import {useLocation, useNavigate} from 'react-router-dom';
+import {MENU_PATH} from '@routes';
 
 const MenuProvider: FC<{children: ReactNode}> = function({children}) {
   const state = useContextReducer();
@@ -13,10 +14,18 @@ const MenuProvider: FC<{children: ReactNode}> = function({children}) {
 };
 
 const PageProvider: FC<{children: ReactNode}> = function({children}) {
-  const location = useLocation();
-  const state = usePageContextReducer(location.state);
+  const {state: locationState} = useLocation();
+  const state = usePageContextReducer(locationState);
+
   const {Provider} = pageContext;
 
+  // 为了清除state 防止刷新后依旧使用state
+  const navigate = useNavigate();
+  useEffect(function() {
+    if (locationState?.pathValue === MENU_PATH)
+      navigate(MENU_PATH);
+  }, [navigate, locationState?.pathValue]);
+
   return <Provider value={state}>{children}</Provider>;
 };
 

+ 12 - 10
packages/app/src/pages/menu/table/hooks.tsx

@@ -1,5 +1,4 @@
 import {useBoolean} from 'ahooks';
-import {useContext, useContextSelector} from 'use-context-selector';
 import {context, pageContext} from '../context';
 import {useState} from 'react';
 import {useMutation, useQuery} from '@tanstack/react-query';
@@ -9,11 +8,12 @@ import {MenuListData} from '@models';
 import {Button, Modal} from 'antd';
 import {ColumnsType} from 'antd/es/table';
 import {MENU_CHILD_PATH, MENU_PATH} from '@routes';
+import {useContext, useContextSection} from '@hooks';
 
 export function useList() {
   const [{page, pageSize}, dispatch] = useContext(pageContext);
   const [count, setCount] = useState(0);
-  const name = useContextSelector(context, state => state[0].name);
+  const name = useContextSection(context, state => state[0].name);
   const {id} = useParams<{id: string}>();
   const pid = id ?? '0';
 
@@ -91,16 +91,19 @@ export function useHandle(data: MenuListData[], refetch: () => void) {
   }
 
   const navigate = useNavigate();
-  const name = useContextSelector(context, state => state[0].name);
-  const {page, pageSize} = useContextSelector(pageContext, state => state[0]);
+  const name = useContextSection(context, state => state[0].name);
+  const {page, pageSize} = useContextSection(pageContext, state => state[0]);
 
-  function onChild(id: string) {
+  function onToChild(id: string) {
     return function() {
+      // 去往二级菜单后 返回时候携带查询信息 确保数据与离开时相同
       const url = generatePath(MENU_CHILD_PATH, {id});
-      navigate(url, {state: {name, page, pageSize}});
+      navigate(url, {state: {name, page, pageSize, pathValue: MENU_PATH}});
     };
   }
 
+  const {id} = useParams<{id: string}>();
+  const isChild = Boolean(id);
   const columns: ColumnsType<MenuListData> = [
     {title: '菜单名称', dataIndex: 'name', key: 'name'},
     {title: '菜单路径', dataIndex: 'url', key: 'url'},
@@ -127,13 +130,13 @@ export function useHandle(data: MenuListData[], refetch: () => void) {
             >
               删除
             </Button>
-            <Button
+            {!isChild && <Button
               type='text'
               loading={pendingId === id}
-              onClick={onChild(id)}
+              onClick={onToChild(id)}
             >
               子菜单
-            </Button>
+            </Button>}
           </>
         );
       },
@@ -145,7 +148,6 @@ export function useHandle(data: MenuListData[], refetch: () => void) {
 
 export function useBack() {
   const {id} = useParams<{id: string}>();
-
   const isChild = Boolean(id);
 
   const {state} = useLocation();

+ 1 - 0
packages/app/src/utils/equal.ts

@@ -1 +1,2 @@
 export {shallow as shallowEqual} from 'zustand/shallow';
+export {default as deepEqual} from 'fast-deep-equal';

+ 2 - 1
pnpm-lock.yaml

@@ -54,6 +54,7 @@ importers:
       antd: ^5.1.7
       axios: ^1.2.6
       classnames: ^2.3.2
+      fast-deep-equal: ^3.1.3
       immer: ^9.0.19
       lottie-react: ^2.3.1
       react: ^18.2.0
@@ -73,6 +74,7 @@ importers:
       antd: 5.1.7_biqbaboplfbrettd7655fr4n2y
       axios: 1.2.6
       classnames: 2.3.2
+      fast-deep-equal: 3.1.3
       immer: 9.0.19
       lottie-react: 2.3.1_biqbaboplfbrettd7655fr4n2y
       react: 18.2.0
@@ -5350,7 +5352,6 @@ packages:
 
   /fast-deep-equal/3.1.3:
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
-    dev: true
 
   /fast-glob/3.2.12:
     resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}