React中如何在render函数中使用异步初始化的TableCommandingController方法
Great question! The core problem you're facing is that your TableCommandingController loads asynchronously, but React's render function runs synchronously—so when render executes, the controller isn't ready yet. And since React doesn't allow async render functions, we need to work around this with state management and method wrapping.
Approach 1: Preload Controller on Mount (State-Driven Render)
This approach loads the controller when the component mounts, updates the component state once it's ready, and triggers a re-render to pass the controller's methods to the Table component. Users won't wait for loading when they trigger actions, since the controller is ready upfront.
Modified TableViewController Code:
import * as React from 'react'; import type { TableCommandingController } from './TableCommandingController'; import type { InsertArgument } from './your-types'; export class TableViewController extends BaseViewController { // Add state to track the loaded controller state = { tableCommandingController: undefined as TableCommandingController | undefined, }; // Cache the controller to avoid reloading/reinitializing private cachedController?: TableCommandingController; constructor(props: any) { super(props); } // Load controller when component mounts async componentDidMount() { const controller = await this.getCommandingController(); this.setState({ tableCommandingController: controller }); } protected getCommandingController = async () => { if (this.cachedController) return this.cachedController; // Lazy load with webpack magic comment const { TableCommanding } = await import(/* webpackChunkName: "TableCommandingController" */ './TableCommandingController'); this.cachedController = new TableCommanding(this.props); return this.cachedController; }; render() { const { tableCommandingController } = this.state; const { /* your props */ } = this.props; const { /* your state properties */ } = this.state; // Show loading state while controller is loading if (!tableCommandingController) { return <div>Loading table controls...</div>; } return ( <Table onInsertRow={tableCommandingController.onInsertRow} onDeleteRow={tableCommandingController.onDeleteRow} onInsertColumn={tableCommandingController.onInsertColumn} onDeleteColumn={tableCommandingController.onDeleteColumn} {/* Other table props */} /> ); } }
Approach 2: On-Demand Loading (Wrapped Callback Methods)
If you don't want to preload the controller (e.g., it's large and users might not trigger table actions), you can wrap each callback method to ensure the controller is loaded only when the action is first triggered. The downside is a small wait time for the first action, but subsequent actions will use the cached controller.
Modified TableViewController Code:
import * as React from 'react'; import type { TableCommandingController } from './TableCommandingController'; import type { InsertArgument } from './your-types'; export class TableViewController extends BaseViewController { // Cache the controller to avoid reloading private cachedController?: TableCommandingController; constructor(props: any) { super(props); } protected getCommandingController = async () => { if (this.cachedController) return this.cachedController; const { TableCommanding } = await import(/* webpackChunkName: "TableCommandingController" */ './TableCommandingController'); this.cachedController = new TableCommanding(this.props); return this.cachedController; }; // Wrap each callback to load controller on first use protected onInsertRow = async (arg: InsertArgument) => { const controller = await this.getCommandingController(); await controller.onInsertRow(arg); }; protected onDeleteRow = async (arg: InsertArgument) => { const controller = await this.getCommandingController(); await controller.onDeleteRow(arg); }; protected onInsertColumn = async (arg: InsertArgument) => { const controller = await this.getCommandingController(); await controller.onInsertColumn(arg); }; protected onDeleteColumn = async (arg: InsertArgument) => { const controller = await this.getCommandingController(); await controller.onDeleteColumn(arg); }; render() { const { /* your props */ } = this.props; const { /* your state properties */ } = this.state; // No loading state needed—callbacks handle loading on first use return ( <Table onInsertRow={this.onInsertRow} onDeleteRow={this.onDeleteRow} onInsertColumn={this.onInsertColumn} onDeleteColumn={this.onDeleteColumn} {/* Other table props */} /> ); } }
Key Notes:
- Caching: Both approaches use a
cachedControllervariable to avoid reloading the module or reinitializing the controller multiple times. - State Updates: In Approach 1, updating the state triggers a re-render once the controller is ready, ensuring
renderhas access to the controller's methods. - User Experience: Choose Approach 1 if you want instant action responses, or Approach 2 if you want to reduce initial load time for components where table actions are used infrequently.
内容的提问来源于stack exchange,提问作者Devendra Verma

