You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件代码优化与请求中断问题技术求助

Hey there! Let's tackle your React component issues step by step. I'll help you streamline the code, boost performance, and fix those lingering AJAX requests when the component unmounts.


1. Fix Pending Requests on Component Unmount

The mounted flag approach is a workaround—it skips state updates but doesn't actually cancel the request. To truly stop in-flight requests, we'll use an AbortController (most modern request libraries, including custom ones like myRequestor, support this). Here's how it works:

  • Initialize an AbortController in the constructor
  • Pass its signal to each request
  • Call abort() in componentWillUnmount to cancel all pending requests

2. Optimize Performance in submitHandler

Your current code loops through the response array multiple times (find, map, sort). We can cut down on unnecessary iterations and reduce re-renders by:

  • Using some() instead of find() when we only need to check for existence (faster, stops at the first match)
  • Chaining map() and sort() into a single operation (cleaner, same performance but more readable)
  • Merging multiple setState calls into one to avoid extra re-renders

3. Streamline Overall Code

We'll clean up redundant code, simplify state updates, and extract reusable logic to make the component more maintainable:

  • Reduce duplicate error-handling code into a helper function
  • Simplify array transformations in fetchInitial
  • Merge multiple setState calls in event handlers

Optimized Full Code

import React from 'react';
import PropTypes from 'prop-types';
import ApplicationLoadingOverlay from './path-to-ApplicationLoadingOverlay'; // Adjust import paths as needed
import ReadinessComponentView from './path-to-ReadinessComponentView';
import Grid from './path-to-Grid';
import Heading from './path-to-Heading';
import Divider from './path-to-Divider';
import Text from './path-to-Text';
import Tenant from './path-to-Tenant';
import ModalManagerExample from './path-to-ModalManagerExample';
import InputField from './path-to-InputField';
import InputElement from './path-to-InputElement';
import Submit from './path-to-Submit';
import Notification from './path-to-Notification';
import { MyRequestorContext } from './path-to-MyRequestorContext';

const propTypes = {
  name: PropTypes.string,
  tenant: PropTypes.arrayOf(PropTypes.any),
  myRequestor: PropTypes.object, // eslint-disable-line react/forbid-prop-types
};

let myRequestor = null;

const CallerUtil = () => {
  myRequestor = React.useContext(MyRequestorContext);
  return <ApplicationLoadingOverlay isOpen backgroundStyle="clear" />;
};

class ReadinessComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      body: '',
      tenant: null,
      error: null,
      errorMsg: '',
      isOpen: false,
      showTable: false,
      isInvalid: false,
      isLoading: false,
      tenantId: '',
      items: [],
      tenantItems: null,
    };
    // Initialize AbortController for request cancellation
    this.abortController = new AbortController();

    // Bind methods
    this.onChangeTenantDropDown = this.onChangeTenantDropDown.bind(this);
    this.onSubmitHandler = this.onSubmitHandler.bind(this);
    this.onChangeHandler = this.onChangeHandler.bind(this);
    this.tenantIdHandler = this.tenantIdHandler.bind(this);
    this.onClose = this.onClose.bind(this);
    this.addNewTenant = this.addNewTenant.bind(this);
    this.fetchInitial = this.fetchInitial.bind(this);
    this.handleError = this.handleError.bind(this);
  }

  componentDidMount() {
    this.setState({ isLoading: true });
    this.fetchInitial();
  }

  componentWillUnmount() {
    // Cancel all pending requests when component unmounts
    this.abortController.abort();
  }

  // Helper function to handle error states consistently
  handleError(errorMsg) {
    this.setState({
      error: 'Failure',
      errorMsg,
      isLoading: false,
      isOpen: true,
      showTable: false,
      body: '',
      tenantId: '',
    });
  }

  onChangeTenantDropDown(value) {
    // Find tenant info in one go, then update state once
    const tenantInfo = this.state.tenantItems.find(t => t.name === value);
    this.setState({
      tenant: value,
      tenantId: tenantInfo?.id || '',
    });
  }

  onSubmitHandler() {
    if (!this.state.body.trim()) {
      this.setState({ isInvalid: true });
      return;
    }

    this.setState({ isLoading: true });
    const params = {
      tenantId: this.state.tenantId,
      tenantShortName: this.state.tenant,
      contactName: this.state.body,
    };

    const { request } = myRequestor.get({
      url: '/getReadinessCheck',
      params,
      signal: this.abortController.signal, // Pass abort signal
    });

    request
      .then(({ data }) => {
        // Skip logic if request was aborted
        if (this.abortController.signal.aborted) return;

        // Use some() instead of find() for faster existence check
        const hasTenantError = data.some(vrsn => vrsn.name === 'TENANT_ERROR');
        if (!hasTenantError) {
          this.handleError('Invalid Tenant');
          return;
        }

        // Chain map and sort for cleaner code
        const readinessResp = data
          .map(version => 
            version.latest 
              ? { ...version, name: `${version.name}_LATEST` } 
              : version
          )
          .sort((a, b) => {
            const textA = a.name.toUpperCase();
            const textB = b.name.toUpperCase();
            return textA.localeCompare(textB);
          });

        // Update state once with all changes
        this.setState({
          items: readinessResp,
          error: 'Success',
          showTable: true,
          isLoading: false,
          body: '',
          tenantId: '',
        });
      })
      .catch(error => {
        // Skip error handling if request was aborted
        if (this.abortController.signal.aborted) return;
        this.handleError('Failure');
      });
  }

  onChangeHandler(event) {
    this.setState({ body: event.target.value, isInvalid: false }); // Reset invalid state on input
  }

  tenantIdHandler(event) {
    this.setState({ tenantId: event.target.value });
  }

  onClose() {
    this.setState(prevState => ({ isOpen: !prevState.isOpen }));
  }

  addNewTenant(shortName, tenantId) {
    this.setState(prevState => ({
      tenantItems: [...prevState.tenantItems, { name: shortName, id: tenantId }],
    }));
  }

  fetchInitial() {
    this.setState({ isLoading: true });
    const { request } = myRequestor.get({
      url: '/tenants',
      signal: this.abortController.signal, // Pass abort signal
    });

    request
      .then(({ data }) => {
        if (this.abortController.signal.aborted) return;

        if (data[0]?.error) {
          this.setState({
            isLoading: false,
            tenantItems: [{ name: 'Default', id: 'Default' }],
          });
          this.handleError(data[0].error);
          return;
        }

        // Simplify tenant list transformation
        const tenants = data.map(tenant => ({
          name: tenant.shortName,
          id: tenant.key,
        }));

        this.setState({
          tenantItems: tenants,
          isLoading: false,
        });
      })
      .catch(error => {
        if (this.abortController.signal.aborted) return;
        this.handleError('Failed to fetch tenants');
      });
  }

  render() {
    const { error, isOpen, body, showTable, tenantItems, isLoading } = this.state;

    if (!tenantItems) {
      return <CallerUtil />;
    }

    if (isLoading) {
      return <ApplicationLoadingOverlay isOpen backgroundStyle="clear" />;
    }

    if (showTable) {
      return (
        <div>
          <h1>Readiness Status</h1>
          <ReadinessComponentView readinessInfo={this.state.items} />
        </div>
      );
    }

    return (
      <div className="ruleSupportEvaluation">
        <Grid>
          <Grid.Row>
            <Grid.Column>
              <Heading level={1}>Readiness Check</Heading>
            </Grid.Column>
          </Grid.Row>
          <Grid.Row>
            <Grid.Column className="ruleSupportEvaluation" large={3} medium={3}>
              <div>
                <Heading className="info" level={3}>About</Heading>
                <Divider />
                <Text className="description" fontSize={18}>Readiness Check</Text>
              </div>
            </Grid.Column>
            <Grid.Column className="ruleSupportEvaluation" large={8} medium={8}>
              <div>
                <div className="readinessComponent">
                  <Tenant tenant={tenantItems} change={this.onChangeTenantDropDown} />
                  <ModalManagerExample addNewTenant={this.addNewTenant} />
                </div>
                <InputField
                  type="text"
                  label="Tenant-Id"
                  value={this.state.tenantId}
                  placeholder="Tenant Id"
                  onChange={this.tenantIdHandler}
                />
                <InputElement
                  isInvalid={this.state.isInvalid}
                  change={this.onChangeHandler}
                  value={body}
                />
                <Submit click={this.onSubmitHandler} />
                {error === 'Failure' && (
                  <Notification
                    errorMessage={this.state.errorMsg}
                    close={this.onClose}
                    isOpen={isOpen}
                  />
                )}
              </div>
            </Grid.Column>
          </Grid.Row>
        </Grid>
      </div>
    );
  }
}

ReadinessComponent.propTypes = propTypes;
export default ReadinessComponent;

Key Improvements Breakdown

  • Request Cancellation: Uses AbortController to truly stop pending requests when the component unmounts, preventing unnecessary network traffic and state update warnings.
  • Performance:
    • Replaced find() with some() for existence checks (stops iterating at the first match).
    • Merged multiple setState calls to minimize re-renders.
    • Streamlined array transformations with chained methods.
  • Maintainability:
    • Extracted error handling into a reusable handleError function to reduce code duplication.
    • Simplified state updates in event handlers.
    • Added checks for aborted requests to avoid unnecessary logic execution.
    • Cleaned up redundant variable declarations.

内容的提问来源于stack exchange,提问作者Gitesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 14:37:33