mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-07-20 10:41:52 +00:00
Do not inject components into other components
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
import type { IContainer } from 'bottlejs';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
const ContainerContext = createContext<IContainer | null>(null);
|
||||
|
||||
export const ContainerProvider = ContainerContext.Provider;
|
||||
|
||||
export const useDependencies = <T extends unknown[]>(...names: string[]): T => {
|
||||
const container = useContext(ContainerContext);
|
||||
if (!container) {
|
||||
throw new Error('You cannot use "useDependencies" outside of a ContainerProvider');
|
||||
}
|
||||
|
||||
return names.map((name) => {
|
||||
const dependency = container[name];
|
||||
if (!dependency) {
|
||||
throw new Error(`Dependency with name "${name}" not found in container`);
|
||||
}
|
||||
|
||||
return dependency;
|
||||
}) as T;
|
||||
};
|
||||
|
||||
// TODO Create Higher Order Component that can pull dependencies from the container
|
||||
60
src/container/context.tsx
Normal file
60
src/container/context.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { IContainer } from 'bottlejs';
|
||||
import { type ComponentType, createContext, useContext } from 'react';
|
||||
|
||||
const ContainerContext = createContext<IContainer | null>(null);
|
||||
|
||||
export const ContainerProvider = ContainerContext.Provider;
|
||||
|
||||
const useContainer = (wrapperName: string): IContainer => {
|
||||
const container = useContext(ContainerContext);
|
||||
if (!container) {
|
||||
throw new Error(`You cannot use "${wrapperName}" outside of a ContainerProvider`);
|
||||
}
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook used to extract dependencies from the container in other hooks.
|
||||
*/
|
||||
export const useDependencies = <T extends unknown[]>(...names: string[]): T => {
|
||||
const container = useContainer('useDependencies');
|
||||
|
||||
return names.map((name) => {
|
||||
const dependency = container[name];
|
||||
if (!dependency) {
|
||||
throw new Error(`Dependency with name "${name}" not found in container`);
|
||||
}
|
||||
|
||||
return dependency;
|
||||
}) as T;
|
||||
};
|
||||
|
||||
/**
|
||||
* Higher Order Component used to inject services into components as props.
|
||||
*/
|
||||
export function withDependencies<
|
||||
Props extends Record<string, unknown>,
|
||||
DependencyName extends string & keyof Props,
|
||||
>(
|
||||
Component: ComponentType<Props>,
|
||||
dependencyNames: DependencyName[],
|
||||
): ComponentType<Omit<Props, DependencyName>> {
|
||||
function Wrapper(props: Omit<Props, DependencyName>) {
|
||||
const container = useContainer('withDependencies');
|
||||
|
||||
// Inject services, unless they have been overridden by props passed from
|
||||
// the parent component.
|
||||
const dependencies: Partial<Record<DependencyName, unknown>> = {};
|
||||
for (const dependency of dependencyNames) {
|
||||
if (!(dependency in props)) {
|
||||
dependencies[dependency] = container[dependency];
|
||||
}
|
||||
}
|
||||
|
||||
const propsWithServices = { ...dependencies, ...props } as Props;
|
||||
return <Component {...propsWithServices} />;
|
||||
}
|
||||
|
||||
return Wrapper;
|
||||
}
|
||||
@@ -2,13 +2,6 @@ import { useTimeoutToggle } from '@shlinkio/shlink-frontend-kit';
|
||||
import { FetchHttpClient } from '@shlinkio/shlink-js-sdk/fetch';
|
||||
import Bottle from 'bottlejs';
|
||||
import { buildShlinkApiClient } from '../api/services/ShlinkApiClientBuilder';
|
||||
import { AppFactory } from '../app/App';
|
||||
import { Home } from '../common/Home';
|
||||
import { ShlinkWebComponentContainerFactory } from '../common/ShlinkWebComponentContainer';
|
||||
import { CreateServerFactory } from '../servers/CreateServer';
|
||||
import { ImportServersBtnFactory } from '../servers/helpers/ImportServersBtn';
|
||||
import { withoutSelectedServer } from '../servers/helpers/withoutSelectedServer';
|
||||
import { ManageServersFactory } from '../servers/ManageServers';
|
||||
import { ServersExporter } from '../servers/services/ServersExporter';
|
||||
import { ServersImporter } from '../servers/services/ServersImporter';
|
||||
import { csvToJson, jsonToCsv } from '../utils/helpers/csvjson';
|
||||
@@ -35,22 +28,5 @@ bottle.serviceFactory('useTimeoutToggle', () => useTimeoutToggle);
|
||||
|
||||
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'HttpClient');
|
||||
|
||||
// Components
|
||||
bottle.factory('App', AppFactory);
|
||||
|
||||
bottle.serviceFactory('Home', () => Home);
|
||||
bottle.decorator('Home', withoutSelectedServer);
|
||||
|
||||
bottle.factory('ShlinkWebComponentContainer', ShlinkWebComponentContainerFactory);
|
||||
|
||||
bottle.factory('ManageServers', ManageServersFactory);
|
||||
bottle.decorator('ManageServers', withoutSelectedServer);
|
||||
|
||||
bottle.factory('CreateServer', CreateServerFactory);
|
||||
bottle.decorator('CreateServer', withoutSelectedServer);
|
||||
|
||||
bottle.factory('ImportServersBtn', ImportServersBtnFactory);
|
||||
|
||||
// Services
|
||||
bottle.service('ServersImporter', ServersImporter, 'csvToJson');
|
||||
bottle.service('ServersExporter', ServersExporter, 'Storage', 'window', 'jsonToCsv');
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { IContainer } from 'bottlejs';
|
||||
import type { FC } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export type FCWithDeps<Props, Deps> = FC<Props> & Partial<Deps>;
|
||||
|
||||
export function useDependencies<Deps>(obj: Deps): Omit<Required<Deps>, keyof FC> {
|
||||
return useMemo(() => obj as Omit<Required<Deps>, keyof FC>, [obj]);
|
||||
}
|
||||
|
||||
export function componentFactory<Deps, CompType = Omit<Partial<Deps>, keyof FC>>(
|
||||
Component: CompType,
|
||||
deps: ReadonlyArray<keyof CompType>,
|
||||
) {
|
||||
return (container: IContainer, console = globalThis.console) => {
|
||||
deps.forEach((dep) => {
|
||||
const resolvedDependency = container[dep as string];
|
||||
if (!resolvedDependency && process.env.NODE_ENV !== 'production') {
|
||||
console.error(`[Debug] Could not find "${dep as string}" dependency in container`);
|
||||
}
|
||||
|
||||
Component[dep] = resolvedDependency;
|
||||
});
|
||||
|
||||
return Component;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user