From a4e6ed5398bcf7c292b0119afedf008109952d3e Mon Sep 17 00:00:00 2001 From: Aleksei Potsetsuev Date: Sun, 22 Sep 2024 19:30:59 +0800 Subject: [PATCH] docs(di-react): add documentation for main API and usage examples --- .vscode/tasks.json | 7 + packages/di-react/package.json | 2 +- packages/docs/.vitepress/data/sidebar.ts | 17 +++ packages/docs/package.json | 5 + packages/docs/src/index.md | 6 +- .../src/packages/di/integrations/index.md | 13 ++ .../src/packages/di/integrations/react/api.md | 133 +++++++++++++++++ .../packages/di/integrations/react/install.md | 69 +++++++++ .../di/integrations/react/overview.md | 36 +++++ .../packages/di/integrations/react/usage.md | 139 ++++++++++++++++++ packages/docs/src/packages/overview.md | 10 +- yarn.lock | 7 +- 12 files changed, 439 insertions(+), 5 deletions(-) create mode 100644 packages/docs/src/packages/di/integrations/index.md create mode 100644 packages/docs/src/packages/di/integrations/react/api.md create mode 100644 packages/docs/src/packages/di/integrations/react/install.md create mode 100644 packages/docs/src/packages/di/integrations/react/overview.md create mode 100644 packages/docs/src/packages/di/integrations/react/usage.md diff --git a/.vscode/tasks.json b/.vscode/tasks.json index edbea39..8dc0ff0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -17,6 +17,13 @@ "script": "clear", "problemMatcher": [] }, + { + "label": "dev: docs", + "type": "npm", + "path": "packages/docs", + "script": "dev", + "problemMatcher": [] + }, { "label": "test: all", "type": "npm", diff --git a/packages/di-react/package.json b/packages/di-react/package.json index cf97381..4ff4ba4 100644 --- a/packages/di-react/package.json +++ b/packages/di-react/package.json @@ -6,7 +6,7 @@ "type": "module", "license": "MIT", "author": "Wroud", - "homepage": "https://wroud.dev/guide/package/di-bindings/react/introduction", + "homepage": "https://wroud.dev/packages/di/integrations/react/overview", "repository": { "type": "git", "url": "https://github.com/Wroud/foundation", diff --git a/packages/docs/.vitepress/data/sidebar.ts b/packages/docs/.vitepress/data/sidebar.ts index 425835f..3642777 100644 --- a/packages/docs/.vitepress/data/sidebar.ts +++ b/packages/docs/.vitepress/data/sidebar.ts @@ -101,6 +101,23 @@ export const sidebar: DefaultTheme.Sidebar = { { text: "Installation", link: "install" }, { text: "Usage", link: "usage" }, { text: "API", link: "api" }, + { + text: "Integrations", + base: "/packages/di/", + link: "integrations", + items: [ + { + text: "React", + base: "/packages/di/integrations/react/", + items: [ + { text: "Overview", link: "overview" }, + { text: "Installation", link: "install" }, + { text: "Usage", link: "usage" }, + { text: "API", link: "api" }, + ], + }, + ], + }, ], }, ], diff --git a/packages/docs/package.json b/packages/docs/package.json index 5adf7ad..a6dcdd0 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -6,10 +6,15 @@ "devDependencies": { "@shikijs/vitepress-twoslash": "^1", "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", "@vitejs/plugin-vue": "^5", "@wroud/di": "workspace:*", + "@wroud/di-react": "workspace:*", "@wroud/di-tools-analyzer": "workspace:*", "@wroud/tsconfig": "workspace:*", + "react": "^18", + "react-dom": "^18", "unocss": "^0", "unplugin-vue-components": "^0", "vite": "^5", diff --git a/packages/docs/src/index.md b/packages/docs/src/index.md index 98757c5..cc7c20a 100644 --- a/packages/docs/src/index.md +++ b/packages/docs/src/index.md @@ -11,8 +11,11 @@ hero: text: Get Started link: /guide/package/di/getting-started/introduction - theme: alt - text: '@wroud/di' + text: "@wroud/di" link: /packages/overview + - theme: alt + text: "@wroud/di-react" + link: packages/di/integrations/react/overview image: src: /icon-big.svg alt: Wroud Foundation @@ -39,4 +42,3 @@ features: title: Simple by Default details: Get started quickly without needing extensive knowledge, thanks to straightforward and intuitive design. --- - diff --git a/packages/docs/src/packages/di/integrations/index.md b/packages/docs/src/packages/di/integrations/index.md new file mode 100644 index 0000000..6f9c14e --- /dev/null +++ b/packages/docs/src/packages/di/integrations/index.md @@ -0,0 +1,13 @@ +--- +outline: deep +--- + +# Integrations + +`@wroud/di` is a flexible dependency injection (DI) system designed to be compatible with a variety of JavaScript environments. Currently, the following integration is available: + +## React Integration ([`@wroud/di-react`](./react/overview.md)) + +The `@wroud/di-react` package integrates the `@wroud/di` library with React, allowing developers to inject services directly into React components. It offers hooks such as `useService` for easy service resolution and supports advanced features like lazy loading through React Suspense. + +This integration simplifies managing dependencies in React applications while maintaining a small bundle size and high performance. diff --git a/packages/docs/src/packages/di/integrations/react/api.md b/packages/docs/src/packages/di/integrations/react/api.md new file mode 100644 index 0000000..d1f8e23 --- /dev/null +++ b/packages/docs/src/packages/di/integrations/react/api.md @@ -0,0 +1,133 @@ +--- +outline: deep +--- + +# API + +This article provides an overview of the main API for the `@wroud/di-react` package. It explains the key components and hooks available for managing dependency injection in React applications. + +## ServiceProvider + +The `ServiceProvider` component is used to supply a service provider context to child components, enabling them to resolve services. + +### Props + +- **`provider: IServiceProvider`** + + - The service provider instance used to resolve services within the component tree. + +### Example + +```tsx +import { ServiceProvider } from "@wroud/di-react"; +import { Main } from "./Main.js"; +import { getServiceProvider } from "./getServiceProvider.js"; + +export function App() { + return ( + +
+ + ); +} +``` + +## useService + +The `useService` hook is used to resolve a single service instance. If the service is lazy-loaded, React's Suspense mechanism will handle its resolution. + +### Arguments + +- **`type: SingleServiceType`** + + - The service type to resolve. + +### Example + +```tsx +import { useService } from "@wroud/di-react"; +import Logger from "./Logger.js"; + +function SomeComponent() { + const logger = useService(Logger); + + function handleClick() { + logger.log("Hello World!"); + } + + return ( + + ); +} +``` + +## useServices + +The `useServices` hook is used to resolve multiple instances of a service. Like `useService`, it utilizes React's Suspense mechanism to handle lazy-loaded services. + +### Arguments + +- **`type: SingleServiceType`** + + - The service type to resolve. + +### Example + +```tsx +import { useServices } from "@wroud/di-react"; +import Logger from "./Logger.js"; + +function SomeComponent() { + const loggers = useServices(Logger); + + function handleClick() { + loggers.forEach((logger) => { + logger.log("Hello World!"); + }); + } + + return ( + + ); +} +``` + +## useServiceCreateAsyncScope + +The `useServiceCreateAsyncScope` hook creates an asynchronous service scope. It requires a `ServiceProvider` to be present in the parent components to function properly. + +### Example + +```tsx +import { useServiceCreateAsyncScope, ServiceProvider } from "@wroud/di-react"; + +function SomeComponent() { + const scopeServiceProvider = useServiceCreateAsyncScope(); + + return ...; +} +``` + +## useServiceCreateScope + +The `useServiceCreateScope` hook creates a new service scope. Similar to `useServiceCreateAsyncScope`, it requires a `ServiceProvider` in the parent components. + +### Example + +```tsx +import { useServiceCreateScope, ServiceProvider } from "@wroud/di-react"; + +function SomeComponent() { + const scopeServiceProvider = useServiceCreateScope(); + + return ...; +} +``` + +--- + +This overview provides a quick reference to the key components and hooks in the `@wroud/di-react` package. For more detailed information, please refer to the full documentation. diff --git a/packages/docs/src/packages/di/integrations/react/install.md b/packages/docs/src/packages/di/integrations/react/install.md new file mode 100644 index 0000000..c7a8cec --- /dev/null +++ b/packages/docs/src/packages/di/integrations/react/install.md @@ -0,0 +1,69 @@ +--- +outline: deep +--- + +# Installation + + + +Install with npm: + +::: code-group + +```sh [npm] +npm install @wroud/di-react +``` + +```sh [yarn] +yarn add @wroud/di-react +``` + +```sh [pnpm] +pnpm add @wroud/di-react +``` + +```sh [bun] +bun add @wroud/di-react +``` + +::: + +## Usage + +### Shorthands + +The `@wroud/di-react` package provides useful shorthand methods. For example, `useService` simplifies injecting a service instance directly into a React component. Here’s a quick example: + +```tsx twoslash +import React from "react"; +import { ServiceContainerBuilder } from "@wroud/di"; +import { ServiceProvider, useService } from "@wroud/di-react"; + +const builder = new ServiceContainerBuilder(); + +class Logger { + log(message: string) { + console.log(message); + } +} + +builder.addSingleton(Logger); + +const serviceProvider = builder.build(); + +function App() { + return ( + + + + ); +} + +function Log() { + const logger = useService(Logger); + logger.log("Hello world!"); + // -> Hello world! + + return <>Check the console output.; +} +``` diff --git a/packages/docs/src/packages/di/integrations/react/overview.md b/packages/docs/src/packages/di/integrations/react/overview.md new file mode 100644 index 0000000..b822057 --- /dev/null +++ b/packages/docs/src/packages/di/integrations/react/overview.md @@ -0,0 +1,36 @@ +--- +outline: deep +--- + +# Dependency Injection for React + +## Overview + +`@wroud/di-react` extends the powerful `@wroud/di` library by providing seamless integration with React. It enables dependency injection (DI) in React applications through intuitive components and hooks, inspired by the .NET framework's DI system. This package is designed to simplify service management in React components while supporting modern JavaScript features, including hooks and Suspense for asynchronous service loading. + +## Key Features + +- **React Integration**: Provides React components and hooks for integrating `@wroud/di` into React applications. +- **Suspense for Lazy-Loaded Services**: Automatically leverages React Suspense to defer the resolution of asynchronous services until they are needed, optimizing performance in large applications. +- **Service Scoping**: Create service scopes dynamically for better lifecycle management in React components. +- **Small Bundle Size**: Lightweight package with minimal overhead, designed for React apps. +- **Easy to Use**: Designed for React developers with straightforward integration patterns. +- **Environment Compatibility**: Works with any React environment (browser, server-side rendering, etc.). + +## Advanced Features + +### Service Scoping in React + +Service scoping allows you to create and manage service lifetimes dynamically within React components. Use `useServiceCreateScope` or `useServiceCreateAsyncScope` to manage scoped services in a parent-child component structure. + +### Asynchronous Service Resolution + +`@wroud/di-react` supports lazy loading of services, enhancing performance by only loading services when necessary. React's Suspense mechanism is used to handle these lazy-loaded services, deferring their resolution until the component requires them. + +## Polyfills + +For full compatibility, you may need the following polyfills in certain environments: + +- **WeakMap**: For environments that do not natively support WeakMap. +- **Promise**: Required for environments lacking modern Promise support. +- **Dispose**: For managing explicit resource disposal. [Learn more](https://github.com/tc39/proposal-explicit-resource-management) diff --git a/packages/docs/src/packages/di/integrations/react/usage.md b/packages/docs/src/packages/di/integrations/react/usage.md new file mode 100644 index 0000000..4173c5b --- /dev/null +++ b/packages/docs/src/packages/di/integrations/react/usage.md @@ -0,0 +1,139 @@ +--- +outline: deep +--- + +# Usage + +This page provides a practical example of how to use `@wroud/di-react` for dependency injection in a React application. We’ll walk through setting up a service container, resolving services using React hooks, and leveraging lazy loading with React Suspense for improved performance. + +## Setting Up the Service Container + +To begin, we create a service container using `ServiceContainerBuilder` from `@wroud/di`. This allows us to register services that will be injected later into our React components. + +```tsx twoslash +import { ServiceContainerBuilder } from "@wroud/di"; +import { ServiceProvider } from "@wroud/di-react"; + +// Create the service container +const builder = new ServiceContainerBuilder(); + +// Example service +class Logger { + log(message: string) { + console.log(message); + } +} + +// Register the service as a singleton +builder.addSingleton(Logger); + +// Build the service provider +const serviceProvider = builder.build(); +``` + +## Providing Services in React + +Next, we use the `ServiceProvider` component to wrap the application, making the registered services available throughout the component tree. + +```tsx +import React from "react"; +import { ServiceProvider } from "@wroud/di-react"; +import AppContent from "./AppContent"; + +function App() { + return ( + + + + ); +} + +export default App; +``` + +## Resolving Services with `useService` + +Within your components, you can resolve services using the `useService` hook. Here’s an example where the `Logger` service is used in a component: + +```tsx +import React from "react"; +import { useService } from "@wroud/di-react"; +import Logger from "./Logger"; + +function AppContent() { + const logger = useService(Logger); + + function handleClick() { + logger.log("Button clicked!"); + } + + return ; +} + +export default AppContent; +``` + +## Lazy Loading Services with React Suspense + +`@wroud/di-react` supports lazy loading of services, allowing you to defer the loading of large or rarely used services until they are needed. This is especially useful for improving performance in larger applications. React's Suspense mechanism is automatically used to handle loading states. + +Here’s an example of how to set up lazy-loaded services using `@wroud/di`'s `lazy` method: + +```tsx +import { lazy, ServiceContainerBuilder } from "@wroud/di"; +import { CounterService } from "./CounterService"; +import { ILoggerService } from "./ILoggerService"; +import { ConsoleLoggerService } from "./ConsoleLogService"; +import { IAdministrationService } from "./administration/IAdministrationService"; + +// Create the service provider with lazy-loaded services +export function createServiceProvider() { + const serviceProvider = new ServiceContainerBuilder() + .addSingleton(CounterService) + .addSingleton(ILoggerService, ConsoleLoggerService) + .addSingleton( + IAdministrationService, + lazy(() => + import("./administration/AdministrationService").then( + (m) => m.AdministrationService, + ), + ), + ) + .build(); + + return serviceProvider; +} +``` + +[Try it in the Playground](https://stackblitz.com/edit/wroud-di-react-lazy?file=src%2Fservices%2FcreateServiceProvider.ts) + +In this example, the `AdministrationService` is lazily loaded. When the `IAdministrationService` is requested via `useService`, the loading will be handled using React Suspense, showing a fallback UI while the service is being loaded. + +To resolve the lazy-loaded service in a component, you can use `useService`, and React Suspense will automatically handle the asynchronous nature of the service: + +```tsx +import React, { Suspense } from "react"; +import { useService, ServiceProvider } from "@wroud/di-react"; +import { IAdministrationService } from "./administration/IAdministrationService"; +import { createServiceProvider } from "./createServiceProvider"; + +const serviceProvider = createServiceProvider(); + +function AdministrationComponent() { + const adminService = useService(IAdministrationService); + + return
{adminService.getAdminData()}
; +} + +function App() { + return ( + Loading administration service...}> + + + + + ); +} + +export default App; +``` diff --git a/packages/docs/src/packages/overview.md b/packages/docs/src/packages/overview.md index 37f3c0c..956081e 100644 --- a/packages/docs/src/packages/overview.md +++ b/packages/docs/src/packages/overview.md @@ -9,19 +9,27 @@ The Wroud Foundation offers a suite of tools to help developers implement best p ## Available Packages - **@wroud/di**: A lightweight dependency injection library for JavaScript inspired by [.NET's DI](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) system. Written in TypeScript, it supports modern JavaScript features, including decorators, and provides robust dependency management capabilities. + - **Other Packages**: More tools to come, each aimed at addressing specific challenges in JavaScript development. # Navigation Use the sidebar to navigate through the documentation. Each package has its own section with detailed guides and API references. Here’s a quick overview of what you’ll find: -## Dependency Injection (@wroud/di) +## Dependency Injection (`@wroud/di`) - **[Overview](./di/overview)**: Introduction and key features. - **[Installation](./di/install)**: Step-by-step guide to installing the package. - **[Usage](./di/usage)**: Examples of how to use the package in different environments. - **[API](./di/api)**: Detailed reference of the API provided by the package. +### React Integration (`@wroud/di-react`) + +- **[Overview](./di/integrations/react/overview)**: Introduction and key features. +- **[Installation](./di/integrations/react/install)**: Step-by-step guide to setting it up in your React application. +- **[Usage](./di/integrations/react/usage)**: Practical examples and patterns for using it within React components. +- **[API](./di/integrations/react/api)**: Complete API reference. + ## Future Tools As we expand our toolset, you will find new sections dedicated to each package with similar documentation structures to ensure a consistent and straightforward experience. diff --git a/yarn.lock b/yarn.lock index 93ed426..17c8db6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3244,7 +3244,7 @@ __metadata: languageName: unknown linkType: soft -"@wroud/di-react@workspace:packages/di-react": +"@wroud/di-react@workspace:*, @wroud/di-react@workspace:packages/di-react": version: 0.0.0-use.local resolution: "@wroud/di-react@workspace:packages/di-react" dependencies: @@ -4556,10 +4556,15 @@ __metadata: dependencies: "@shikijs/vitepress-twoslash": "npm:^1" "@types/node": "npm:^20" + "@types/react": "npm:^18" + "@types/react-dom": "npm:^18" "@vitejs/plugin-vue": "npm:^5" "@wroud/di": "workspace:*" + "@wroud/di-react": "workspace:*" "@wroud/di-tools-analyzer": "workspace:*" "@wroud/tsconfig": "workspace:*" + react: "npm:^18" + react-dom: "npm:^18" unocss: "npm:^0" unplugin-vue-components: "npm:^0" vite: "npm:^5"