diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/context.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/context.tsx index d95faf7ae9..c29e5dc28d 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/context.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/context.tsx @@ -1,7 +1,7 @@ 'use client' -import { createContext, useContext } from 'use-context-selector' import type { ModelAndParameter } from '../types' +import { createSelectorCtx } from '@/utils/context' export type DebugWithMultipleModelContextType = { multipleModelConfigs: ModelAndParameter[] @@ -9,13 +9,9 @@ export type DebugWithMultipleModelContextType = { onDebugWithMultipleModelChange: (singleModelConfig: ModelAndParameter) => void checkCanSend?: () => boolean } -const DebugWithMultipleModelContext = createContext({ - multipleModelConfigs: [], - onMultipleModelConfigsChange: () => {}, - onDebugWithMultipleModelChange: () => {}, -}) +const [,useDebugWithMultipleModelContext, DebugWithMultipleModelContext] = createSelectorCtx() -export const useDebugWithMultipleModelContext = () => useContext(DebugWithMultipleModelContext) +export { useDebugWithMultipleModelContext } type DebugWithMultipleModelContextProviderProps = { children: React.ReactNode diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 4e86d06b5b..ae1cc9d816 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -13,7 +13,7 @@ import InfiniteScroll from 'react-infinite-scroll-component' import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' import timezone from 'dayjs/plugin/timezone' -import { createContext, useContext } from 'use-context-selector' +import { useContext } from 'use-context-selector' import { useShallow } from 'zustand/react/shallow' import { useTranslation } from 'react-i18next' import type { ChatItemInTree } from '../../base/chat/types' @@ -45,6 +45,7 @@ import { CopyIcon } from '@/app/components/base/copy-icon' import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils' import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' import { correctProvider } from '@/utils' +import { createSelectorCtx } from '@/utils/context' dayjs.extend(utc) dayjs.extend(timezone) @@ -62,7 +63,7 @@ type IDrawerContext = { appDetail?: App } -const DrawerContext = createContext({} as IDrawerContext) +const [,, DrawerContext] = createSelectorCtx() /** * Icon component with numbers diff --git a/web/app/components/base/chat/chat-with-history/context.tsx b/web/app/components/base/chat/chat-with-history/context.tsx index 1000c4899a..6088267768 100644 --- a/web/app/components/base/chat/chat-with-history/context.tsx +++ b/web/app/components/base/chat/chat-with-history/context.tsx @@ -1,7 +1,6 @@ 'use client' import type { RefObject } from 'react' -import { createContext, useContext } from 'use-context-selector' import type { Callback, ChatConfig, @@ -15,8 +14,9 @@ import type { AppMeta, ConversationItem, } from '@/models/share' +import { createSelectorCtx } from '@/utils/context' -export interface ChatWithHistoryContextValue { +export type ChatWithHistoryContextValue = { appInfoError?: any appInfoLoading?: boolean appMeta?: AppMeta @@ -51,29 +51,4 @@ export interface ChatWithHistoryContextValue { themeBuilder?: ThemeBuilder } -export const ChatWithHistoryContext = createContext({ - currentConversationId: '', - appPrevChatList: [], - pinnedConversationList: [], - conversationList: [], - showConfigPanelBeforeChat: false, - newConversationInputs: {}, - newConversationInputsRef: { current: {} }, - handleNewConversationInputsChange: () => {}, - inputsForms: [], - handleNewConversation: () => {}, - handleStartChat: () => {}, - handleChangeConversation: () => {}, - handlePinConversation: () => {}, - handleUnpinConversation: () => {}, - handleDeleteConversation: () => {}, - conversationRenaming: false, - handleRenameConversation: () => {}, - handleNewConversationCompleted: () => {}, - chatShouldReloadKey: '', - isMobile: false, - isInstalledApp: false, - handleFeedback: () => {}, - currentChatInstanceRef: { current: { handleStop: () => {} } }, -}) -export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext) +export const [, useChatWithHistoryContext, ChatWithHistoryContext] = createSelectorCtx() diff --git a/web/app/components/base/chat/chat/context.tsx b/web/app/components/base/chat/chat/context.tsx index 8c69884c91..d76dd55575 100644 --- a/web/app/components/base/chat/chat/context.tsx +++ b/web/app/components/base/chat/chat/context.tsx @@ -1,8 +1,8 @@ 'use client' import type { ReactNode } from 'react' -import { createContext, useContext } from 'use-context-selector' import type { ChatProps } from './index' +import { createSelectorCtx } from '@/utils/context' export type ChatContextValue = Pick -const ChatContext = createContext({ - chatList: [], -}) +const [, useChatContext, ChatContext] = createSelectorCtx() type ChatContextProviderProps = { children: ReactNode @@ -61,6 +59,6 @@ export const ChatContextProvider = ({ ) } -export const useChatContext = () => useContext(ChatContext) +export { useChatContext } export default ChatContext diff --git a/web/app/components/base/chat/embedded-chatbot/context.tsx b/web/app/components/base/chat/embedded-chatbot/context.tsx index 546f9c7c70..c7c86376d7 100644 --- a/web/app/components/base/chat/embedded-chatbot/context.tsx +++ b/web/app/components/base/chat/embedded-chatbot/context.tsx @@ -1,7 +1,6 @@ 'use client' import type { RefObject } from 'react' -import { createContext, useContext } from 'use-context-selector' import type { ChatConfig, ChatItem, @@ -14,8 +13,9 @@ import type { AppMeta, ConversationItem, } from '@/models/share' +import { createSelectorCtx } from '@/utils/context' -export interface EmbeddedChatbotContextValue { +export type EmbeddedChatbotContextValue = { appInfoError?: any appInfoLoading?: boolean appMeta?: AppMeta @@ -45,24 +45,4 @@ export interface EmbeddedChatbotContextValue { themeBuilder?: ThemeBuilder } -export const EmbeddedChatbotContext = createContext({ - currentConversationId: '', - appPrevChatList: [], - pinnedConversationList: [], - conversationList: [], - showConfigPanelBeforeChat: false, - newConversationInputs: {}, - newConversationInputsRef: { current: {} }, - handleNewConversationInputsChange: () => {}, - inputsForms: [], - handleNewConversation: () => {}, - handleStartChat: () => {}, - handleChangeConversation: () => {}, - handleNewConversationCompleted: () => {}, - chatShouldReloadKey: '', - isMobile: false, - isInstalledApp: false, - handleFeedback: () => {}, - currentChatInstanceRef: { current: { handleStop: () => {} } }, -}) -export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext) +export const [, useEmbeddedChatbotContext, EmbeddedChatbotContext] = createSelectorCtx() diff --git a/web/app/components/base/features/context.tsx b/web/app/components/base/features/context.tsx index 3c9347c0c5..57247e7f39 100644 --- a/web/app/components/base/features/context.tsx +++ b/web/app/components/base/features/context.tsx @@ -1,5 +1,4 @@ import { - createContext, useRef, } from 'react' import type { @@ -7,8 +6,9 @@ import type { FeaturesStore, } from './store' import { createFeaturesStore } from './store' +import { createCtx } from '@/utils/context' -export const FeaturesContext = createContext(null) +export const [, , FeaturesContext] = createCtx() type FeaturesProviderProps = { children: React.ReactNode diff --git a/web/app/components/base/file-uploader/store.tsx b/web/app/components/base/file-uploader/store.tsx index 3ad0b74f05..9980311f0c 100644 --- a/web/app/components/base/file-uploader/store.tsx +++ b/web/app/components/base/file-uploader/store.tsx @@ -1,6 +1,4 @@ import { - createContext, - useContext, useRef, } from 'react' import { @@ -10,6 +8,7 @@ import { import type { FileEntity, } from './types' +import { createCtx } from '@/utils/context' type Shape = { files: FileEntity[] @@ -30,20 +29,13 @@ export const createFileStore = ( } type FileStore = ReturnType -export const FileContext = createContext(null) +export const [, useFileStore, FileContext] = createCtx() export function useStore(selector: (state: Shape) => T): T { - const store = useContext(FileContext) - if (!store) - throw new Error('Missing FileContext.Provider in the tree') - + const store = useFileStore() return useZustandStore(store, selector) } -export const useFileStore = () => { - return useContext(FileContext)! -} - type FileProviderProps = { children: React.ReactNode value?: FileEntity[] diff --git a/web/app/components/base/portal-to-follow-elem/index.tsx b/web/app/components/base/portal-to-follow-elem/index.tsx index 3d24c6ee99..82d3bf38f5 100644 --- a/web/app/components/base/portal-to-follow-elem/index.tsx +++ b/web/app/components/base/portal-to-follow-elem/index.tsx @@ -17,6 +17,7 @@ import { import type { OffsetOptions, Placement } from '@floating-ui/react' import cn from '@/utils/classnames' +import { createCtx } from '@/utils/context' export type PortalToFollowElemOptions = { /* * top, bottom, left, right @@ -78,18 +79,9 @@ export function usePortalToFollowElem({ ) } -type ContextType = ReturnType | null +type ContextType = ReturnType -const PortalToFollowElemContext = React.createContext(null) - -export function usePortalToFollowElemContext() { - const context = React.useContext(PortalToFollowElemContext) - - if (context == null) - throw new Error('PortalToFollowElem components must be wrapped in ') - - return context -} +const [, usePortalToFollowElemContext, PortalToFollowElemContext] = createCtx() export function PortalToFollowElem({ children, diff --git a/web/app/components/base/toast/index.tsx b/web/app/components/base/toast/index.tsx index 3e13db5d7f..449edf8467 100644 --- a/web/app/components/base/toast/index.tsx +++ b/web/app/components/base/toast/index.tsx @@ -8,8 +8,8 @@ import { InformationCircleIcon, XCircleIcon, } from '@heroicons/react/20/solid' -import { createContext, useContext } from 'use-context-selector' import classNames from '@/utils/classnames' +import { createSelectorCtx } from '@/utils/context' export type IToastProps = { type?: 'success' | 'error' | 'warning' | 'info' @@ -23,8 +23,7 @@ type IToastContext = { notify: (props: IToastProps) => void } -export const ToastContext = createContext({} as IToastContext) -export const useToastContext = () => useContext(ToastContext) +export const [, useToastContext, ToastContext] = createSelectorCtx() const Toast = ({ type = 'info', message, @@ -46,7 +45,7 @@ const Toast = ({ type === 'info' ? 'bg-blue-50' : '', )}>
-
+
{type === 'success' &&