@ebay/muse-lib-react 1.3.2 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # README
2
2
 
3
- @ebay/muse-lib-react is a lib plugin maintained by Muse team with a combination of popular React tech stacks like:
3
+ @ebay/muse-lib-react is a lib plugin with a combination of popular React tech stacks like:
4
4
 
5
5
  - [React Router](https://reactrouter.com/en/main)
6
6
  - [Redux](https://redux.js.org/)
@@ -0,0 +1,621 @@
1
+ # Plugin Integration Guide: @ebay/muse-lib-react
2
+
3
+ **Generated**: 2026-03-28
4
+ **Plugin Type**: lib
5
+
6
+ ---
7
+
8
+ ## 1. Plugin Purpose & Overview
9
+
10
+ `@ebay/muse-lib-react` is the **foundational React library plugin** for MUSE applications. As a lib-type plugin with app entry capabilities, it serves three critical roles:
11
+
12
+ 1. **React Application Bootstrap** - Creates the React root element, initializes the application, and renders the main component tree with all providers (Redux, React Router, React Query, Nice Modal)
13
+
14
+ 2. **Shared Module Provider** - Exports commonly-used React libraries as shared modules to prevent duplicate dependencies across plugins:
15
+ - `react-loadable` - Code splitting and lazy loading
16
+ - `lodash` - Utility functions
17
+ - `react-use` - Collection of React hooks
18
+ - `react-router-dom` v6 - Client-side routing
19
+ - `@tanstack/react-query` v4 - Server state management
20
+
21
+ 3. **Extension Point Foundation** - Defines comprehensive extension points enabling other plugins to:
22
+ - Customize routing and homepage
23
+ - Extend Redux store with plugin-specific reducers
24
+ - Modify the provider stack
25
+ - Define main layout and root-level components
26
+ - Hook into application lifecycle events
27
+
28
+ ### Key Features
29
+
30
+ - **Provider Stack Management**: Ordered provider chain (React Query → Redux → SubApp Context → Nice Modal → React Router) with extension points at every stage
31
+ - **Flexible Routing**: Supports Browser, Hash, and Memory routers with React Router v6, backwards-compatible with v3-style route configuration
32
+ - **Redux Integration**: Pre-configured Redux store with Thunk middleware, Redux Logger (dev), and DevTools support
33
+ - **Sub-App Support**: Built-in iframe-based sub-application system for embedding external MUSE apps
34
+ - **Lifecycle Hooks**: Extension points for initialization (`beforeRender`, `afterRender`, `onReady`)
35
+
36
+ ---
37
+
38
+ ## 2. Extension Points Exposed
39
+
40
+ This plugin exposes extension points that allow other plugins to customize and extend the React application. Extension points are organized into four categories:
41
+
42
+ ### 2.1 Application Lifecycle
43
+
44
+ Extension points for hooking into application initialization:
45
+
46
+ #### `root.beforeRender`
47
+ - **Type**: `Function`
48
+ - **Purpose**: Execute logic before the React root element is created and rendered
49
+ - **Use Case**: Early initialization tasks like setting up global state, registering event listeners, or configuring third-party libraries
50
+ - **Invocation**: Called in `src/index.js:26` before `createRoot()`
51
+ - **Context**: Runs after all plugins are loaded but before any React rendering
52
+
53
+ #### `root.afterRender`
54
+ - **Type**: `Function`
55
+ - **Purpose**: Execute logic immediately after the React root element is rendered
56
+ - **Use Case**: Post-render initialization like focusing elements, starting analytics, or triggering initial data fetches
57
+ - **Invocation**: Called in `src/index.js:31` after `root.render(<Root />)`
58
+ - **Context**: Runs before `onReady`, React tree is mounted but may not be fully painted
59
+
60
+ #### `onReady`
61
+ - **Type**: `Function`
62
+ - **Purpose**: Execute logic after the application is fully mounted to the DOM
63
+ - **Use Case**: Final initialization tasks that require the full component tree to be ready
64
+ - **Invocation**: Called in `src/index.js:33` as the last step of `renderApp()`
65
+ - **Context**: Complete application ready, all components mounted
66
+
67
+ ### 2.2 Routing & Navigation
68
+
69
+ Extension points for customizing application routes and routing behavior:
70
+
71
+ #### `route`
72
+ - **Type**: `MuseRoute | MuseRoute[]`
73
+ - **Purpose**: Register route definitions for plugin-specific pages
74
+ - **Use Case**: Add new pages/routes to the application
75
+ - **Collection**: Routes collected in `src/common/routeConfig.js:87` via `plugin.invoke('!route')`
76
+ - **Route Structure**:
77
+ - `path`: URL path (string or array of strings)
78
+ - `component`: React component to render
79
+ - `childRoutes`: Nested routes
80
+ - `parent`: ID of parent route (moves route to parent's `childRoutes`)
81
+ - `isIndex`: Mark as index route (duplicated to path='')
82
+ - `id`: Unique identifier for route (enables `parent` references)
83
+ - **Special Behavior**:
84
+ - Routes with `path` starting with `/` are moved to top level
85
+ - Routes with `parent` property are relocated to parent's `childRoutes`
86
+ - Array paths are expanded to multiple route objects
87
+ - **File Reference**: `src/common/routeConfig.js:32-132`
88
+
89
+ #### `routerProps`
90
+ - **Type**: `Record<string, any>`
91
+ - **Purpose**: Merge additional props into the React Router `RouterProvider` component
92
+ - **Use Case**: Customize router behavior (e.g., future flags, error handlers)
93
+ - **Collection**: First contribution used via `plugin.invoke('!routerProps')[0]` in `src/Root.js:140`
94
+ - **Note**: Props are merged with defaults (`basename`, `router`, `navigator`)
95
+ - **File Reference**: `src/Root.js:140-177`
96
+
97
+ ### 2.3 Provider Stack Customization
98
+
99
+ Extension points for modifying the provider chain that wraps the application. Providers execute in order from outer to inner (lower order = outer wrapper).
100
+
101
+ #### `root.preProcessProviders`
102
+ - **Type**: `(context: { providers: ProviderType[] }) => void`
103
+ - **Purpose**: Modify or remove built-in providers before custom providers are collected
104
+ - **Use Case**: Disable default providers (e.g., remove Redux if using alternative state management)
105
+ - **Invocation Order**: 1st - called by `extendArray()` in `src/Root.js:181`
106
+ - **Context**: `{ providers }` array of default providers (React Query, Redux, SubApp Context, Nice Modal, React Router)
107
+ - **File Reference**: `src/utils.js:17`, `src/Root.js:144-181`
108
+
109
+ #### `root.getProviders`
110
+ - **Type**: `(context: { providers: ProviderType[] }) => ProviderType | ProviderType[] | void`
111
+ - **Purpose**: Contribute additional providers to wrap the application
112
+ - **Use Case**: Add custom providers (e.g., theme provider, i18n provider, custom context)
113
+ - **Invocation Order**: 2nd - called by `extendArray()` in `src/Root.js:181`
114
+ - **Return**: Single provider, array of providers, or nothing
115
+ - **Provider Structure**:
116
+ - `order`: Rendering order (lower = outer, default sort)
117
+ - `key`: Unique identifier
118
+ - `provider`: React component (must accept `children` prop)
119
+ - `props`: Props object for provider
120
+ - `renderProvider`: Custom render function `(children) => ReactNode` (alternative to `provider`)
121
+ - **File Reference**: `src/utils.js:18`, `src/Root.js:144-192`
122
+
123
+ #### `root.processProviders`
124
+ - **Type**: `(context: { providers: ProviderType[] }) => void`
125
+ - **Purpose**: Modify the collected providers array after all contributions
126
+ - **Use Case**: Reorder providers, modify provider props, or conditionally add/remove providers
127
+ - **Invocation Order**: 3rd - called by `extendArray()` in `src/Root.js:181`
128
+ - **Context**: `{ providers }` includes both built-in and contributed providers
129
+ - **File Reference**: `src/utils.js:20`, `src/Root.js:181`
130
+
131
+ #### `root.postProcessProviders`
132
+ - **Type**: `(context: { providers: ProviderType[] }) => void`
133
+ - **Purpose**: Final opportunity to modify providers before rendering
134
+ - **Use Case**: Last-minute adjustments or validations
135
+ - **Invocation Order**: 4th (final) - called by `extendArray()` in `src/Root.js:181`
136
+ - **Note**: After this, providers are sorted by `order` and rendered
137
+ - **File Reference**: `src/utils.js:21`, `src/Root.js:181`
138
+
139
+ #### `root.renderChildren`
140
+ - **Type**: `(children: ReactNode) => ReactNode`
141
+ - **Purpose**: Wrap the root element with custom components
142
+ - **Use Case**: Alternative to `getProviders` for wrapping the app (less recommended)
143
+ - **Collection**: All contributions applied sequentially in `src/Root.js:105-110`
144
+ - **Note**: Prefer `getProviders` for adding providers; this is for non-provider wrappers
145
+ - **File Reference**: `src/Root.js:104-111`
146
+
147
+ ### 2.4 Layout & UI
148
+
149
+ Extension points for customizing the application's visual structure:
150
+
151
+ #### `home.homepage`
152
+ - **Type**: `ComponentType<Object>`
153
+ - **Purpose**: Provide a custom homepage component for the root route `/`
154
+ - **Use Case**: Replace default "Welcome to Muse" page with application-specific homepage
155
+ - **Resolution**: Single contribution used; if multiple, checks `window.MUSE_GLOBAL.homepage` for preference
156
+ - **Collection**: Via `plugin.getPlugins('home.homepage')` in `src/common/routeConfig.js:96-111`
157
+ - **Default**: If not provided, uses built-in `Homepage` component (`src/features/home/Homepage.js`)
158
+ - **File Reference**: `src/common/routeConfig.js:94-116`
159
+
160
+ #### `home.mainLayout`
161
+ - **Type**: `ComponentType<{ children: ReactNode }>`
162
+ - **Purpose**: Provide a main layout component that wraps all routes
163
+ - **Use Case**: Define application shell (header, sidebar, footer) that persists across pages
164
+ - **Collection**: Via `plugin.invoke('!home.mainLayout')` in `src/features/home/App.js:10`
165
+ - **Constraint**: Only one layout allowed; multiple contributions will show error message
166
+ - **Note**: When using custom layout, undeploy `@ebay/muse-layout-antd` if present
167
+ - **File Reference**: `src/features/home/App.js:9-22`
168
+
169
+ #### `rootComponent`
170
+ - **Type**: `ComponentType<Object>`
171
+ - **Purpose**: Render initialization components at app root (should return `null`, no UI)
172
+ - **Use Case**: Global initialization logic that needs component lifecycle (e.g., modal managers, global listeners)
173
+ - **Collection**: All contributions rendered in `src/features/home/App.js:26-29`
174
+ - **Important**: Components should not render visible UI, only invisible initialization/context
175
+ - **File Reference**: `src/features/home/App.js:6-30`
176
+
177
+ ### 2.5 State Management
178
+
179
+ Extension points for extending the Redux store:
180
+
181
+ #### `reducer`
182
+ - **Type**: `Reducer<any, AnyAction>`
183
+ - **Purpose**: Contribute a plugin-level reducer to the Redux store
184
+ - **Use Case**: Add plugin-specific state management to Redux
185
+ - **Storage Key**: Auto-generated as `camelCase('plugin-' + pluginName)`
186
+ - **Example**: Plugin `@ebay/my-plugin` → state key `pluginEbayMyPlugin`
187
+ - **Collection**: Via `plugin.getPlugins('reducer')` in `src/common/rootReducer.js:24-31`
188
+ - **File Reference**: `src/common/rootReducer.js:23-31`
189
+
190
+ #### `reducers`
191
+ - **Type**: `Record<string, Reducer<any, AnyAction>>`
192
+ - **Purpose**: Contribute root-level reducers with custom state keys
193
+ - **Use Case**: Add multiple reducers or control exact state key names
194
+ - **Example**: `{ myData: myDataReducer, config: configReducer }` → `state.myData`, `state.config`
195
+ - **Collection**: Via `plugin.getPlugins('reducers')` in `src/common/rootReducer.js:33-37`
196
+ - **Note**: Provides more control than `reducer` extension point
197
+ - **File Reference**: `src/common/rootReducer.js:33-39`
198
+
199
+ ---
200
+
201
+ ## 3. Extension Points Contributed
202
+
203
+ This plugin does not contribute to extension points from other plugins. As a foundational lib plugin, it defines the extension point architecture but does not extend external plugins.
204
+
205
+ ---
206
+
207
+ ## 4. Exported Functionality
208
+
209
+ As a lib-type plugin, `@ebay/muse-lib-react` exports shared modules and utilities for use by other plugins.
210
+
211
+ ### 4.1 Shared Modules (Module Federation)
212
+
213
+ These modules are exported via the default export in `src/index.js:41` and made available to other plugins through MUSE's module sharing system (webpack/vite plugin externalization):
214
+
215
+ #### `Loadable`
216
+ - **Source**: `react-loadable` package
217
+ - **Purpose**: Code splitting and lazy loading of React components
218
+ - **Use Case**: Improve bundle size and load time by dynamically importing components
219
+ - **File Reference**: `src/index.js:7,41`
220
+
221
+ #### `_` (lodash)
222
+ - **Source**: `lodash` package
223
+ - **Purpose**: Utility functions for arrays, objects, strings, etc.
224
+ - **Use Case**: Common data manipulation without importing lodash separately
225
+ - **File Reference**: `src/index.js:8,41`
226
+
227
+ #### `reactUse`
228
+ - **Source**: `react-use` package
229
+ - **Purpose**: Collection of essential React hooks
230
+ - **Use Case**: Access common hooks (useMount, useToggle, etc.) without separate package
231
+ - **File Reference**: `src/index.js:9,41`
232
+
233
+ #### `reactRouterDom`
234
+ - **Source**: `react-router-dom` v6
235
+ - **Purpose**: React Router v6 routing library
236
+ - **Use Case**: Navigation components (`Link`, `Navigate`) and hooks (`useNavigate`, `useParams`)
237
+ - **File Reference**: `src/index.js:11,41`
238
+
239
+ #### `reactQuery`
240
+ - **Source**: `@tanstack/react-query` v4
241
+ - **Purpose**: Server state management (data fetching, caching, synchronization)
242
+ - **Use Case**: Manage API calls and server state with hooks (`useQuery`, `useMutation`)
243
+ - **File Reference**: `src/index.js:12,41`
244
+
245
+ ### 4.2 Utility Components
246
+
247
+ These components can be imported from the plugin's `build/` directory by other plugins:
248
+
249
+ #### `Nodes`
250
+ - **Source**: `src/features/common/Nodes.js`
251
+ - **Type**: `ComponentType<{ items: any[], extName: string, extBase: string, extArgs: any }>`
252
+ - **Purpose**: Render a list of components with extension point integration
253
+ - **Use Case**: Create extendable UI component lists (e.g., toolbar buttons, menu items)
254
+ - **Behavior**: Uses `extendArray()` to allow plugins to contribute additional nodes
255
+ - **Exported**: `src/features/common/index.js:3`
256
+ - **File Reference**: `src/features/common/Nodes.js:1-21`
257
+
258
+ #### `useExtPoint`
259
+ - **Source**: `src/features/common/useExtPoint.js`
260
+ - **Type**: `(extPointName: string, extArgs?: any) => { extNode: ReactNode, values: any[] }`
261
+ - **Purpose**: React hook for consuming extension points within components
262
+ - **Use Case**: Render extension point contributions as React elements and collect callback values
263
+ - **Returns**:
264
+ - `extNode`: React fragment containing all extension point components
265
+ - `values`: Array of values passed to callbacks
266
+ - **Exported**: `src/features/common/index.js:4`
267
+ - **File Reference**: `src/features/common/useExtPoint.js:1-29`
268
+
269
+ #### `ErrorBoundary`
270
+ - **Source**: `src/features/common/ErrorBoundary.js`
271
+ - **Type**: `ComponentType<{ children: ReactNode }>`
272
+ - **Purpose**: React error boundary component
273
+ - **Use Case**: Catch and handle React errors gracefully
274
+ - **Exported**: `src/features/common/index.js:2`
275
+
276
+ #### `PageNotFound`
277
+ - **Source**: `src/features/common/PageNotFound.js`
278
+ - **Type**: `ComponentType<Object>`
279
+ - **Purpose**: Default 404 page component
280
+ - **Use Case**: Fallback for unmatched routes
281
+ - **Exported**: `src/features/common/index.js:1`
282
+
283
+ ### 4.3 Sub-App Utilities
284
+
285
+ Components and utilities for the sub-application feature:
286
+
287
+ #### `SubAppContainer`
288
+ - **Source**: `src/features/sub-app/SubAppContainer.js`
289
+ - **Type**: `ComponentType<{ subApp: SubAppConfig, subApps: SubAppConfig[] }>`
290
+ - **Purpose**: Renders an external MUSE app in an iframe
291
+ - **Use Case**: Embed other MUSE applications within the main app
292
+ - **Exported**: `src/features/sub-app/index.js:1`
293
+
294
+ #### `FixedSubAppContainer`
295
+ - **Source**: `src/features/sub-app/FixedSubAppContainer.js`
296
+ - **Type**: `ComponentType<Object>`
297
+ - **Purpose**: Fixed-position sub-app container
298
+ - **Use Case**: Persistent sub-app that doesn't follow routing
299
+ - **Exported**: `src/features/sub-app/index.js:2`
300
+
301
+ #### `SubAppContext`
302
+ - **Source**: `src/features/sub-app/SubAppContext.js`
303
+ - **Type**: `React.Context<any>`
304
+ - **Purpose**: React context for parent-child sub-app communication
305
+ - **Use Case**: Share data between parent app and embedded sub-apps
306
+ - **Exported**: `src/features/sub-app/index.js:4`
307
+
308
+ #### `LoadingSkeleton`
309
+ - **Source**: `src/features/sub-app/LoadingSkeleton.js`
310
+ - **Type**: `ComponentType<Object>`
311
+ - **Purpose**: Loading placeholder for sub-apps
312
+ - **Use Case**: Display loading state while sub-app loads
313
+ - **Exported**: `src/features/sub-app/index.js:3`
314
+
315
+ #### `C2SProxyFailed`
316
+ - **Source**: `src/features/sub-app/C2SProxyFailed.js`
317
+ - **Type**: `ComponentType<Object>`
318
+ - **Purpose**: Error component for sub-app proxy failures
319
+ - **Use Case**: Display when C2S proxy configuration fails
320
+ - **Exported**: `src/features/sub-app/index.js:5`
321
+
322
+ #### `useSetSubAppState`
323
+ - **Source**: `src/features/sub-app/redux/setSubAppState.js`
324
+ - **Type**: `() => (state: any) => void`
325
+ - **Purpose**: Hook for setting sub-app state from Redux
326
+ - **Use Case**: Update sub-app context from parent app
327
+ - **Exported**: `src/features/sub-app/redux/hooks.js:1`
328
+
329
+ ### 4.4 Common Utilities
330
+
331
+ #### `extendArray`
332
+ - **Source**: `src/utils.js:15-24`
333
+ - **Type**: `(arr: any[], extName: string, extBase: string, ...args: any[]) => any[]`
334
+ - **Purpose**: Makes an array extensible via js-plugin extension points
335
+ - **Use Case**: Internal utility for provider/route extension, can be used by other plugins for custom extension points
336
+ - **Behavior**: Invokes `preProcess`, `get`, `process`, `postProcess` extension points, flattens contributions, sorts by order
337
+ - **Example**:
338
+ ```javascript
339
+ const items = [];
340
+ extendArray(items, 'items', 'myPlugin.customExt', { context: 'data' });
341
+ // Invokes: myPlugin.customExt.preProcessItems
342
+ // myPlugin.customExt.getItems
343
+ // myPlugin.customExt.processItems
344
+ // myPlugin.customExt.postProcessItems
345
+ ```
346
+ - **Exported**: `src/utils.js:26`
347
+ - **File Reference**: `src/utils.js:15-24`
348
+
349
+ ### 4.5 Redux Store Access
350
+
351
+ #### `store`
352
+ - **Source**: `src/common/store.js`
353
+ - **Type**: Redux Store wrapper
354
+ - **Purpose**: Access to the global Redux store
355
+ - **Use Case**: Dispatch actions or access state outside React components
356
+ - **Methods**:
357
+ - `getStore()`: Returns the Redux store instance
358
+ - `getState()`: Returns current state
359
+ - `dispatch(action)`: Dispatches an action
360
+ - `subscribe(listener)`: Subscribes to store changes
361
+ - `replaceReducer(reducer)`: Hot-swaps reducers
362
+ - **File Reference**: `src/common/store.js:1-21`
363
+
364
+ #### `history`
365
+ - **Source**: `src/common/history.js`
366
+ - **Type**: History object (from `history` package)
367
+ - **Purpose**: Programmatic navigation outside React components
368
+ - **Use Case**: Navigate to routes from Redux actions or non-React code
369
+ - **File Reference**: `src/common/history.js`
370
+
371
+ ---
372
+
373
+ ## 5. Integration Examples
374
+
375
+ **CRITICAL**: Extension points are **nested object properties**, NOT string paths!
376
+
377
+ ### Example 1: Adding a Custom Route
378
+
379
+ ```javascript
380
+ // ✅ CORRECT - nested object properties
381
+ plugin.register({
382
+ name: 'my-plugin',
383
+ route: {
384
+ path: '/my-page',
385
+ component: MyPageComponent,
386
+ childRoutes: [
387
+ {
388
+ path: 'details/:id',
389
+ component: DetailsComponent
390
+ }
391
+ ]
392
+ }
393
+ });
394
+
395
+ // ❌ INCORRECT - DO NOT use string paths
396
+ plugin.register({
397
+ name: 'my-plugin',
398
+ 'route': { ... } // Works, but 'route' doesn't need quotes
399
+ });
400
+ ```
401
+
402
+ ### Example 2: Contributing Multiple Routes
403
+
404
+ ```javascript
405
+ plugin.register({
406
+ name: 'my-plugin',
407
+ route: [
408
+ { path: '/page1', component: Page1 },
409
+ { path: '/page2', component: Page2 },
410
+ { path: '/page3', component: Page3, parent: 'some-route-id' }
411
+ ]
412
+ });
413
+ ```
414
+
415
+ ### Example 3: Customizing Homepage
416
+
417
+ ```javascript
418
+ import MyHomepage from './MyHomepage';
419
+
420
+ plugin.register({
421
+ name: 'my-plugin',
422
+ home: {
423
+ homepage: MyHomepage
424
+ }
425
+ });
426
+ ```
427
+
428
+ ### Example 4: Adding Custom Layout
429
+
430
+ ```javascript
431
+ import MyLayout from './MyLayout';
432
+
433
+ plugin.register({
434
+ name: 'my-plugin',
435
+ home: {
436
+ mainLayout: MyLayout // Should accept children prop
437
+ }
438
+ });
439
+ ```
440
+
441
+ ### Example 5: Adding Redux Reducer (Plugin-Level)
442
+
443
+ ```javascript
444
+ import myReducer from './redux/reducer';
445
+
446
+ plugin.register({
447
+ name: '@ebay/my-plugin',
448
+ reducer: myReducer
449
+ });
450
+
451
+ // Redux state will be accessible at:
452
+ // state.pluginEbayMyPlugin
453
+ ```
454
+
455
+ ### Example 6: Adding Redux Reducers (Root-Level)
456
+
457
+ ```javascript
458
+ import userReducer from './redux/userReducer';
459
+ import configReducer from './redux/configReducer';
460
+
461
+ plugin.register({
462
+ name: 'my-plugin',
463
+ reducers: {
464
+ user: userReducer,
465
+ appConfig: <USER_NAME>
466
+ }
467
+ });
468
+
469
+ // Redux state accessible at:
470
+ // state.user
471
+ // state.appConfig
472
+ ```
473
+
474
+ ### Example 7: Adding a Custom Provider
475
+
476
+ ```javascript
477
+ import { ThemeProvider } from 'my-theme-library';
478
+
479
+ plugin.register({
480
+ name: 'my-plugin',
481
+ root: {
482
+ getProviders: () => ({
483
+ order: 15, // Between React Query (10) and Redux (20)
484
+ key: 'theme-provider',
485
+ provider: ThemeProvider,
486
+ props: { theme: myTheme }
487
+ })
488
+ }
489
+ });
490
+ ```
491
+
492
+ ### Example 8: Modifying Existing Providers
493
+
494
+ ```javascript
495
+ plugin.register({
496
+ name: 'my-plugin',
497
+ root: {
498
+ preProcessProviders: ({ providers }) => {
499
+ // Remove Redux provider
500
+ const reduxIndex = providers.findIndex(p => p.key === 'redux-provider');
501
+ if (reduxIndex >= 0) {
502
+ providers.splice(reduxIndex, 1);
503
+ }
504
+ }
505
+ }
506
+ });
507
+ ```
508
+
509
+ ### Example 9: Lifecycle Hooks
510
+
511
+ ```javascript
512
+ plugin.register({
513
+ name: 'my-plugin',
514
+ root: {
515
+ beforeRender: () => {
516
+ console.log('App is about to render');
517
+ // Initialize analytics, set up listeners, etc.
518
+ },
519
+ afterRender: () => {
520
+ console.log('App has rendered');
521
+ // Post-render tasks
522
+ }
523
+ },
524
+ onReady: () => {
525
+ console.log('App is fully ready');
526
+ // Final initialization
527
+ }
528
+ });
529
+ ```
530
+
531
+ ### Example 10: Using Shared Modules
532
+
533
+ ```javascript
534
+ // In your plugin code
535
+ const { exports } = plugin.getPlugin('@ebay/muse-lib-react');
536
+ const { _, reactRouterDom, reactQuery } = exports;
537
+
538
+ // Use lodash
539
+ const uniqueItems = _.uniq(items);
540
+
541
+ // Use React Router
542
+ const { useNavigate, Link } = reactRouterDom;
543
+
544
+ // Use React Query
545
+ const { useQuery, useMutation } = reactQuery;
546
+ ```
547
+
548
+ ### Example 11: Using Utility Components
549
+
550
+ ```javascript
551
+ import { Nodes, useExtPoint } from '@ebay/muse-lib-react/build/features/common';
552
+
553
+ // Using Nodes component
554
+ function MyToolbar() {
555
+ const items = [
556
+ { key: 'btn1', component: Button1, props: { label: 'Click' } }
557
+ ];
558
+
559
+ return (
560
+ <Nodes
561
+ items={items}
562
+ extName="buttons"
563
+ extBase="myPlugin.toolbar"
564
+ />
565
+ );
566
+ }
567
+
568
+ // Using useExtPoint hook
569
+ function MyComponent() {
570
+ const { extNode, values } = useExtPoint('myPlugin.customPoint', { data: 123 });
571
+
572
+ return <div>{extNode}</div>;
573
+ }
574
+ ```
575
+
576
+ ### Example 12: Root Component for Initialization
577
+
578
+ ```javascript
579
+ function MyInitComponent() {
580
+ useEffect(() => {
581
+ // Initialize something globally
582
+ window.myGlobalState = { ... };
583
+ }, []);
584
+
585
+ return null; // IMPORTANT: No UI rendering
586
+ }
587
+
588
+ plugin.register({
589
+ name: 'my-plugin',
590
+ rootComponent: MyInitComponent
591
+ });
592
+ ```
593
+
594
+ ### Example 13: Custom Router Props
595
+
596
+ ```javascript
597
+ plugin.register({
598
+ name: 'my-plugin',
599
+ routerProps: {
600
+ future: {
601
+ v7_startTransition: true
602
+ }
603
+ }
604
+ });
605
+ ```
606
+
607
+ ---
608
+
609
+ ## Integration Checklist
610
+
611
+ When integrating with `@ebay/muse-lib-react`:
612
+
613
+ - [ ] Ensure plugin type is compatible (normal or lib plugins can extend this)
614
+ - [ ] Use nested object properties for extension points, not string paths
615
+ - [ ] Only one plugin should provide `home.homepage` and `home.mainLayout`
616
+ - [ ] Provider `order` values: lower = outer wrapper (10, 20, 30...)
617
+ - [ ] Redux reducer keys via `reducer` are auto-generated; use `reducers` for custom keys
618
+ - [ ] `rootComponent` must return `null` (no UI)
619
+ - [ ] Routes with absolute paths (`/`) are moved to top level
620
+ - [ ] Shared modules accessed via `plugin.getPlugin('@ebay/muse-lib-react').exports`
621
+ - [ ] Sub-app configuration goes in `window.MUSE_GLOBAL.plugins` under this plugin's `subApps` array