Do not inject components into other components

This commit is contained in:
Alejandro Celaya
2025-11-15 11:46:19 +01:00
parent dad3990c23
commit d10bea50bc
17 changed files with 156 additions and 197 deletions

View File

@@ -1,23 +1,22 @@
import type { ResultProps,TimeoutToggle } from '@shlinkio/shlink-frontend-kit';
import { Button, Result,useToggle } from '@shlinkio/shlink-frontend-kit';
import type { ResultProps, TimeoutToggle } from '@shlinkio/shlink-frontend-kit';
import { Button, Result, useToggle } from '@shlinkio/shlink-frontend-kit';
import type { FC } from 'react';
import { useCallback, useState } from 'react';
import { useNavigate } from 'react-router';
import { NoMenuLayout } from '../common/NoMenuLayout';
import type { FCWithDeps } from '../container/utils';
import { componentFactory, useDependencies } from '../container/utils';
import { withDependencies } from '../container/context';
import { useGoBack } from '../utils/helpers/hooks';
import type { ServerData } from './data';
import { ensureUniqueIds } from './helpers';
import { DuplicatedServersModal } from './helpers/DuplicatedServersModal';
import type { ImportServersBtnProps } from './helpers/ImportServersBtn';
import { ImportServersBtn } from './helpers/ImportServersBtn';
import { ServerForm } from './helpers/ServerForm';
import { withoutSelectedServer } from './helpers/withoutSelectedServer';
import { useServers } from './reducers/servers';
const SHOW_IMPORT_MSG_TIME = 4000;
type CreateServerDeps = {
ImportServersBtn: FC<ImportServersBtnProps>;
export type CreateServerProps = {
useTimeoutToggle: TimeoutToggle;
};
@@ -30,15 +29,12 @@ const ImportResult = ({ variant }: Pick<ResultProps, 'variant'>) => (
</div>
);
const CreateServer: FCWithDeps<any, CreateServerDeps> = () => {
const CreateServerBase: FC<CreateServerProps> = withoutSelectedServer(({ useTimeoutToggle }) => {
const { servers, createServers } = useServers();
const { ImportServersBtn, useTimeoutToggle } = useDependencies(CreateServer);
const navigate = useNavigate();
const goBack = useGoBack();
const hasServers = !!Object.keys(servers).length;
// eslint-disable-next-line react-compiler/react-compiler
const [serversImported, setServersImported] = useTimeoutToggle({ delay: SHOW_IMPORT_MSG_TIME });
// eslint-disable-next-line react-compiler/react-compiler
const [errorImporting, setErrorImporting] = useTimeoutToggle({ delay: SHOW_IMPORT_MSG_TIME });
const { flag: isConfirmModalOpen, toggle: toggleConfirmModal } = useToggle();
const [serverData, setServerData] = useState<ServerData>();
@@ -83,6 +79,6 @@ const CreateServer: FCWithDeps<any, CreateServerDeps> = () => {
/>
</NoMenuLayout>
);
};
});
export const CreateServerFactory = componentFactory(CreateServer, ['ImportServersBtn', 'useTimeoutToggle']);
export const CreateServer = withDependencies(CreateServerBase, ['useTimeoutToggle']);

View File

@@ -5,27 +5,24 @@ import { Button, Result, SearchInput, SimpleCard, Table } from '@shlinkio/shlink
import type { FC } from 'react';
import { useMemo, useState } from 'react';
import { NoMenuLayout } from '../common/NoMenuLayout';
import type { FCWithDeps } from '../container/utils';
import { componentFactory, useDependencies } from '../container/utils';
import type { ImportServersBtnProps } from './helpers/ImportServersBtn';
import { withDependencies } from '../container/context';
import { ImportServersBtn } from './helpers/ImportServersBtn';
import { withoutSelectedServer } from './helpers/withoutSelectedServer';
import { ManageServersRow } from './ManageServersRow';
import { useServers } from './reducers/servers';
import type { ServersExporter } from './services/ServersExporter';
type ManageServersDeps = {
export type ManageServersProps = {
ServersExporter: ServersExporter;
ImportServersBtn: FC<ImportServersBtnProps>;
useTimeoutToggle: TimeoutToggle;
};
const SHOW_IMPORT_MSG_TIME = 4000;
const ManageServers: FCWithDeps<unknown, ManageServersDeps> = () => {
const {
ServersExporter: serversExporter,
ImportServersBtn,
useTimeoutToggle,
} = useDependencies(ManageServers);
const ManageServersBase: FC<ManageServersProps> = withoutSelectedServer(({
ServersExporter: serversExporter,
useTimeoutToggle,
}) => {
const { servers } = useServers();
const [searchTerm, setSearchTerm] = useState('');
const allServers = useMemo(() => Object.values(servers), [servers]);
@@ -34,7 +31,7 @@ const ManageServers: FCWithDeps<unknown, ManageServersDeps> = () => {
[allServers, searchTerm],
);
const hasAutoConnect = allServers.some(({ autoConnect }) => !!autoConnect);
// eslint-disable-next-line react-compiler/react-compiler
const [errorImporting, setErrorImporting] = useTimeoutToggle({ delay: SHOW_IMPORT_MSG_TIME });
return (
@@ -82,10 +79,6 @@ const ManageServers: FCWithDeps<unknown, ManageServersDeps> = () => {
)}
</NoMenuLayout>
);
};
});
export const ManageServersFactory = componentFactory(ManageServers, [
'ServersExporter',
'ImportServersBtn',
'useTimeoutToggle',
]);
export const ManageServers = withDependencies(ManageServersBase, ['ServersExporter', 'useTimeoutToggle']);

View File

@@ -1,10 +1,9 @@
import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, Tooltip, useToggle , useTooltip } from '@shlinkio/shlink-frontend-kit';
import type { ChangeEvent, PropsWithChildren } from 'react';
import { Button, Tooltip, useToggle, useTooltip } from '@shlinkio/shlink-frontend-kit';
import type { ChangeEvent, FC, PropsWithChildren } from 'react';
import { useCallback, useRef, useState } from 'react';
import type { FCWithDeps } from '../../container/utils';
import { componentFactory, useDependencies } from '../../container/utils';
import { withDependencies } from '../../container/context';
import type { ServerData } from '../data';
import { useServers } from '../reducers/servers';
import type { ServersImporter } from '../services/ServersImporter';
@@ -16,21 +15,20 @@ export type ImportServersBtnProps = PropsWithChildren<{
onError?: (error: Error) => void;
tooltipPlacement?: 'top' | 'bottom';
className?: string;
// Injected
ServersImporter: ServersImporter
}>;
type ImportServersBtnDeps = {
ServersImporter: ServersImporter
};
const ImportServersBtn: FCWithDeps<ImportServersBtnProps, ImportServersBtnDeps> = ({
const ImportServersBtnBase: FC<ImportServersBtnProps> = ({
children,
onImport,
onError = () => {},
tooltipPlacement = 'bottom',
className = '',
ServersImporter: serversImporter,
}) => {
const { createServers, servers } = useServers();
const { ServersImporter: serversImporter } = useDependencies(ImportServersBtn);
const fileInputRef = useRef<HTMLInputElement>(null);
const { anchor, tooltip } = useTooltip({ placement: tooltipPlacement });
const [duplicatedServers, setDuplicatedServers] = useState<ServerData[]>([]);
@@ -106,4 +104,4 @@ const ImportServersBtn: FCWithDeps<ImportServersBtnProps, ImportServersBtnDeps>
);
};
export const ImportServersBtnFactory = componentFactory(ImportServersBtn, ['ServersImporter']);
export const ImportServersBtn = withDependencies(ImportServersBtnBase, ['ServersImporter']);