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

如何修改组件使点击DOM <input>引用符合React.StrictMode规范

解决React StrictMode下文件选择框/相机重复触发问题

在React StrictMode开发模式下,组件的部分生命周期方法会被重复调用(比如shouldComponentUpdate、getDerivedStateFromProps),你的组件之前在shouldComponentUpdate里直接触发文件输入框点击,导致StrictMode下两次弹框;而且原逻辑没做去重,同一个openedBy可能被重复处理。

修改方案

  • 把触发输入框的副作用逻辑从shouldComponentUpdate移到componentDidUpdate,这个生命周期方法在StrictMode下不会被重复调用。
  • 新增状态processedOpenBy跟踪已处理的openedBy值,确保同一个触发指令只会执行一次。

修改后的完整代码

export class CameraTrigger extends React.Component<Props, State> {
  static defaultProps = {
    openedBy: undefined,
  }
  fileInputRef: React.Ref<any>

  constructor(props: Props) {
    super(props)
    this.fileInputRef = React.createRef()
    this.state = {
      lastOpenBy: undefined,
      processedOpenBy: undefined, // 新增:记录已处理的触发标识
    }
  }

  static getDerivedStateFromProps(props: Props, state: State) {
    const update: Partial<State> = {};
    if (props.openedBy) {
      update.lastOpenBy = props.openedBy;
      // 仅当新的openedBy未被处理过时,更新processedOpenBy
      if (props.openedBy !== state.processedOpenBy) {
        update.processedOpenBy = props.openedBy;
      }
    }
    // 当openedBy重置为null时,清空processedOpenBy,允许下次触发
    else if (state.processedOpenBy) {
      update.processedOpenBy = undefined;
    }
    return Object.keys(update).length ? update : null;
  }

  componentDidUpdate(prevProps: Props) {
    const { openedBy } = this.props;
    const inputElement = this.fileInputRef.current;
    
    // 只有当触发指令有效、未被处理过,且输入框DOM存在时,才执行点击
    if (openedBy && this.state.processedOpenBy === openedBy && inputElement) {
      inputElement.click();
      this.props.closeCamera();
    }
  }

  // shouldComponentUpdate仅负责判断是否需要更新组件,不执行副作用
  shouldComponentUpdate(nextProps: Props, nextState: State) {
    return nextProps.openedBy !== this.props.openedBy || nextState.processedOpenBy !== this.state.processedOpenBy;
  }

  clearInputBeforeNextUpload = (input: HTMLInputElement) => {
    input.value = '';
  }

  fileInputChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
    const input = event.currentTarget as HTMLInputElement;

    if (input.files && input.files.length) {
      const file = input.files[0];
      this.props.handleImageFile(file, this.state.lastOpenBy, this.state.lastEncounterId, this.state.lastConversationId);
      this.clearInputBeforeNextUpload(input);
    }
  }

  render() {
    return (
      <input
        name="message-file-upload"
        type="file"
        accept={MimeTypeMap.JPEG}
        capture="environment"
        ref={this.fileInputRef}
        onChange={this.fileInputChanged}
        style={{ display: 'none' }}
      />
    );
  }
}

type Props = {
  handleImageFile: Function;
  closeCamera: Function;
  openedBy?: string;
}

type State = {
  lastOpenBy?: string;
  processedOpenBy?: string; // 新增状态字段
}

关键修改说明

  1. 新增状态跟踪:processedOpenBy用来记录已经处理过的触发标识,避免同一个openedBy被重复处理。
  2. 调整生命周期逻辑:把触发输入框的操作移到componentDidUpdate,StrictMode不会重复调用这个方法,从根源避免两次弹框。
  3. 优化更新判断:shouldComponentUpdate仅在触发标识或openedBy变化时允许组件更新,保留原有的性能优化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 09:44:59