Browse Source

fix: toggling AppDetailNav causes unnecessary component rerenders (#3718)

legao 1 year ago
parent
commit
40e36e9b52

+ 6 - 1
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx

@@ -5,6 +5,7 @@ import React, { useCallback, useEffect, useState } from 'react'
 import { usePathname, useRouter } from 'next/navigation'
 import cn from 'classnames'
 import { useTranslation } from 'react-i18next'
+import { useShallow } from 'zustand/react/shallow'
 import s from './style.module.css'
 import { useStore } from '@/app/components/app/store'
 import AppSideBar from '@/app/components/app-sidebar'
@@ -32,7 +33,11 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
   const media = useBreakpoints()
   const isMobile = media === MediaType.mobile
   const { isCurrentWorkspaceManager } = useAppContext()
-  const { appDetail, setAppDetail, setAppSiderbarExpand } = useStore()
+  const { appDetail, setAppDetail, setAppSiderbarExpand } = useStore(useShallow(state => ({
+    appDetail: state.appDetail,
+    setAppDetail: state.setAppDetail,
+    setAppSiderbarExpand: state.setAppSiderbarExpand,
+  })))
   const [navigation, setNavigation] = useState<Array<{
     name: string
     href: string

+ 2 - 1
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx

@@ -26,7 +26,8 @@ export type ICardViewProps = {
 const CardView: FC<ICardViewProps> = ({ appId }) => {
   const { t } = useTranslation()
   const { notify } = useContext(ToastContext)
-  const { appDetail, setAppDetail } = useAppStore()
+  const appDetail = useAppStore(state => state.appDetail)
+  const setAppDetail = useAppStore(state => state.setAppDetail)
 
   const updateAppDetail = async () => {
     fetchAppDetail({ url: '/apps', id: appId }).then((res) => {

+ 1 - 1
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx

@@ -22,7 +22,7 @@ export type IChartViewProps = {
 
 export default function ChartView({ appId }: IChartViewProps) {
   const { t } = useTranslation()
-  const { appDetail } = useAppStore()
+  const appDetail = useAppStore(state => state.appDetail)
   const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
   const isWorkflow = appDetail?.mode === 'workflow'
   const [period, setPeriod] = useState<PeriodParams>({ name: t('appLog.filter.period.last7days'), query: { start: today.subtract(7, 'day').format(queryDateFormat), end: today.format(queryDateFormat) } })

File diff suppressed because it is too large
+ 248 - 248
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx


+ 13 - 10
web/app/components/app-sidebar/index.tsx

@@ -1,4 +1,5 @@
-import React, { useEffect, useState } from 'react'
+import React, { useEffect } from 'react'
+import { useShallow } from 'zustand/react/shallow'
 import NavLink from './navLink'
 import type { NavIcon } from './navLink'
 import AppBasic from './basic'
@@ -26,11 +27,13 @@ export type IAppDetailNavProps = {
 }
 
 const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => {
-  const { appSidebarExpand, setAppSiderbarExpand } = useAppStore()
+  const { appSidebarExpand, setAppSiderbarExpand } = useAppStore(useShallow(state => ({
+    appSidebarExpand: state.appSidebarExpand,
+    setAppSiderbarExpand: state.setAppSiderbarExpand,
+  })))
   const media = useBreakpoints()
   const isMobile = media === MediaType.mobile
-  const [modeState, setModeState] = useState(appSidebarExpand)
-  const expand = modeState === 'expand'
+  const expand = appSidebarExpand === 'expand'
 
   const handleToggle = (state: string) => {
     setAppSiderbarExpand(state === 'expand' ? 'collapse' : 'expand')
@@ -39,9 +42,9 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
   useEffect(() => {
     if (appSidebarExpand) {
       localStorage.setItem('app-detail-collapse-or-expand', appSidebarExpand)
-      setModeState(appSidebarExpand)
+      setAppSiderbarExpand(appSidebarExpand)
     }
-  }, [appSidebarExpand])
+  }, [appSidebarExpand, setAppSiderbarExpand])
 
   return (
     <div
@@ -61,7 +64,7 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
         )}
         {iconType !== 'app' && (
           <AppBasic
-            mode={modeState}
+            mode={appSidebarExpand}
             iconType={iconType}
             icon={icon}
             icon_background={icon_background}
@@ -81,10 +84,10 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
       >
         {navigation.map((item, index) => {
           return (
-            <NavLink key={index} mode={modeState} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
+            <NavLink key={index} mode={appSidebarExpand} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
           )
         })}
-        {extraInfo && extraInfo(modeState)}
+        {extraInfo && extraInfo(appSidebarExpand)}
       </nav>
       {
         !isMobile && (
@@ -96,7 +99,7 @@ const AppDetailNav = ({ title, desc, icon, icon_background, navigation, extraInf
           >
             <div
               className='flex items-center justify-center w-6 h-6 text-gray-500 cursor-pointer'
-              onClick={() => handleToggle(modeState)}
+              onClick={() => handleToggle(appSidebarExpand)}
             >
               {
                 expand

+ 7 - 1
web/app/components/app/configuration/debug/index.tsx

@@ -6,6 +6,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'
 import { setAutoFreeze } from 'immer'
 import { useBoolean } from 'ahooks'
 import { useContext } from 'use-context-selector'
+import { useShallow } from 'zustand/react/shallow'
 import HasNotSetAPIKEY from '../base/warning-mask/has-not-set-api'
 import FormattingChanged from '../base/warning-mask/formatting-changed'
 import GroupName from '../base/group-name'
@@ -367,7 +368,12 @@ const Debug: FC<IDebug> = ({
     handleVisionConfigInMultipleModel()
   }, [multipleModelConfigs, mode])
 
-  const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal } = useAppStore()
+  const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal } = useAppStore(useShallow(state => ({
+    currentLogItem: state.currentLogItem,
+    setCurrentLogItem: state.setCurrentLogItem,
+    showPromptLogModal: state.showPromptLogModal,
+    setShowPromptLogModal: state.setShowPromptLogModal,
+  })))
   const [width, setWidth] = useState(0)
   const ref = useRef<HTMLDivElement>(null)
 

+ 5 - 1
web/app/components/app/configuration/index.tsx

@@ -8,6 +8,7 @@ import produce from 'immer'
 import { useBoolean, useGetState } from 'ahooks'
 import { clone, isEqual } from 'lodash-es'
 import { CodeBracketIcon } from '@heroicons/react/20/solid'
+import { useShallow } from 'zustand/react/shallow'
 import Button from '../../base/button'
 import Loading from '../../base/loading'
 import AppPublisher from '../app-publisher'
@@ -65,7 +66,10 @@ type PublishConfig = {
 const Configuration: FC = () => {
   const { t } = useTranslation()
   const { notify } = useContext(ToastContext)
-  const { appDetail, setAppSiderbarExpand } = useAppStore()
+  const { appDetail, setAppSiderbarExpand } = useAppStore(useShallow(state => ({
+    appDetail: state.appDetail,
+    setAppSiderbarExpand: state.setAppSiderbarExpand,
+  })))
   const [formattingChanged, setFormattingChanged] = useState(false)
   const { setShowAccountSettingModal } = useModalContext()
   const [hasFetchedDetail, setHasFetchedDetail] = useState(false)

+ 1 - 1
web/app/components/app/log-annotation/index.tsx

@@ -21,7 +21,7 @@ const LogAnnotation: FC<Props> = ({
 }) => {
   const { t } = useTranslation()
   const router = useRouter()
-  const { appDetail } = useAppStore()
+  const appDetail = useAppStore(state => state.appDetail)
 
   const options = [
     { value: PageType.log, text: t('appLog.title') },

+ 9 - 1
web/app/components/base/chat/chat/index.tsx

@@ -12,6 +12,7 @@ import {
 import { useTranslation } from 'react-i18next'
 import { debounce } from 'lodash-es'
 import classNames from 'classnames'
+import { useShallow } from 'zustand/react/shallow'
 import type {
   ChatConfig,
   ChatItem,
@@ -79,7 +80,14 @@ const Chat: FC<ChatProps> = ({
   chatAnswerContainerInner,
 }) => {
   const { t } = useTranslation()
-  const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore()
+  const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({
+    currentLogItem: state.currentLogItem,
+    setCurrentLogItem: state.setCurrentLogItem,
+    showPromptLogModal: state.showPromptLogModal,
+    setShowPromptLogModal: state.setShowPromptLogModal,
+    showAgentLogModal: state.showAgentLogModal,
+    setShowAgentLogModal: state.setShowAgentLogModal,
+  })))
   const [width, setWidth] = useState(0)
   const chatContainerRef = useRef<HTMLDivElement>(null)
   const chatContainerInnerRef = useRef<HTMLDivElement>(null)

+ 1 - 1
web/app/components/develop/index.tsx

@@ -12,7 +12,7 @@ type IDevelopMainProps = {
 }
 
 const DevelopMain = ({ appId }: IDevelopMainProps) => {
-  const { appDetail } = useAppStore()
+  const appDetail = useAppStore(state => state.appDetail)
   const { t } = useTranslation()
 
   if (!appDetail) {

+ 1 - 1
web/app/components/header/app-nav/index.tsx

@@ -40,7 +40,7 @@ const AppNav = () => {
   const { t } = useTranslation()
   const { appId } = useParams()
   const { isCurrentWorkspaceManager } = useAppContext()
-  const { appDetail } = useAppStore()
+  const appDetail = useAppStore(state => state.appDetail)
   const [showNewAppDialog, setShowNewAppDialog] = useState(false)
   const [showNewAppTemplateDialog, setShowNewAppTemplateDialog] = useState(false)
   const [showCreateFromDSLModal, setShowCreateFromDSLModal] = useState(false)

+ 1 - 1
web/app/components/header/nav/index.tsx

@@ -31,7 +31,7 @@ const Nav = ({
   onLoadmore,
   isApp,
 }: INavProps) => {
-  const { setAppDetail } = useAppStore()
+  const setAppDetail = useAppStore(state => state.setAppDetail)
   const [hovered, setHovered] = useState(false)
   const segment = useSelectedLayoutSegment()
   const isActived = Array.isArray(activeSegment) ? activeSegment.includes(segment!) : segment === activeSegment

+ 1 - 1
web/app/components/header/nav/nav-selector/index.tsx

@@ -35,7 +35,7 @@ const NavSelector = ({ curNav, navs, createText, isApp, onCreate, onLoadmore }:
   const { t } = useTranslation()
   const router = useRouter()
   const { isCurrentWorkspaceManager } = useAppContext()
-  const { setAppDetail } = useAppStore()
+  const setAppDetail = useAppStore(state => state.setAppDetail)
 
   const handleScroll = useCallback(debounce((e) => {
     if (typeof onLoadmore === 'function') {

+ 1 - 1
web/app/components/workflow/header/index.tsx

@@ -33,7 +33,7 @@ const Header: FC = () => {
   const workflowStore = useWorkflowStore()
   const appDetail = useAppStore(s => s.appDetail)
   const appSidebarExpand = useAppStore(s => s.appSidebarExpand)
-  const appID = useAppStore(state => state.appDetail?.id)
+  const appID = appDetail?.id
   const {
     nodesReadOnly,
     getNodesReadOnly,

+ 6 - 1
web/app/components/workflow/header/view-history.tsx

@@ -5,6 +5,7 @@ import {
 import cn from 'classnames'
 import useSWR from 'swr'
 import { useTranslation } from 'react-i18next'
+import { useShallow } from 'zustand/react/shallow'
 import {
   useIsChatMode,
   useWorkflow,
@@ -40,7 +41,11 @@ const ViewHistory = () => {
   const [open, setOpen] = useState(false)
   const { formatTimeFromNow } = useWorkflow()
   const workflowStore = useWorkflowStore()
-  const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore()
+  const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({
+    appDetail: state.appDetail,
+    setCurrentLogItem: state.setCurrentLogItem,
+    setShowMessageLogModal: state.setShowMessageLogModal,
+  })))
   const historyWorkflowData = useStore(s => s.historyWorkflowData)
   const { handleBackupDraft } = useWorkflowRun()
   const { data: runList, isLoading: runListLoading } = useSWR((appDetail && !isChatMode && open) ? `/apps/${appDetail.id}/workflow-runs` : null, fetchWorkflowRunHistory)

+ 7 - 1
web/app/components/workflow/panel/index.tsx

@@ -4,6 +4,7 @@ import {
   useMemo,
 } from 'react'
 import { useNodes } from 'reactflow'
+import { useShallow } from 'zustand/react/shallow'
 import type { CommonNodeType } from '../types'
 import { Panel as NodePanel } from '../nodes'
 import { useStore } from '../store'
@@ -22,7 +23,12 @@ const Panel: FC = () => {
   const showInputsPanel = useStore(s => s.showInputsPanel)
   const workflowRunningData = useStore(s => s.workflowRunningData)
   const historyWorkflowData = useStore(s => s.historyWorkflowData)
-  const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal } = useAppStore()
+  const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({
+    currentLogItem: state.currentLogItem,
+    setCurrentLogItem: state.setCurrentLogItem,
+    showMessageLogModal: state.showMessageLogModal,
+    setShowMessageLogModal: state.setShowMessageLogModal,
+  })))
   const {
     showNodePanel,
     showDebugAndPreviewPanel,

+ 1 - 1
web/app/components/workflow/run/index.tsx

@@ -25,7 +25,7 @@ const RunPanel: FC<RunProps> = ({ hideResult, activeTab = 'RESULT', runID, getRe
   const { t } = useTranslation()
   const { notify } = useContext(ToastContext)
   const [currentTab, setCurrentTab] = useState<string>(activeTab)
-  const { appDetail } = useAppStore()
+  const appDetail = useAppStore(state => state.appDetail)
   const [loading, setLoading] = useState<boolean>(true)
   const [runDetail, setRunDetail] = useState<WorkflowRunDetailResponse>()
   const [list, setList] = useState<NodeTracing[]>([])

+ 3 - 3
web/app/components/workflow/store.ts

@@ -1,8 +1,8 @@
 import { useContext } from 'react'
 import {
-  create,
   useStore as useZustandStore,
 } from 'zustand'
+import { createStore } from 'zustand/vanilla'
 import { debounce } from 'lodash-es'
 import type { Viewport } from 'reactflow'
 import type {
@@ -70,9 +70,9 @@ type Shape = {
 }
 
 export const createWorkflowStore = () => {
-  return create<Shape>(set => ({
+  return createStore<Shape>(set => ({
     appId: '',
-    workflowData: undefined,
+    workflowRunningData: undefined,
     setWorkflowRunningData: workflowRunningData => set(() => ({ workflowRunningData })),
     historyWorkflowData: undefined,
     setHistoryWorkflowData: historyWorkflowData => set(() => ({ historyWorkflowData })),