如何修改组件使点击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; // 新增状态字段 }
关键修改说明
- 新增状态跟踪:
processedOpenBy用来记录已经处理过的触发标识,避免同一个openedBy被重复处理。 - 调整生命周期逻辑:把触发输入框的操作移到
componentDidUpdate,StrictMode不会重复调用这个方法,从根源避免两次弹框。 - 优化更新判断:
shouldComponentUpdate仅在触发标识或openedBy变化时允许组件更新,保留原有的性能优化。
内容的提问来源于stack exchange,提问作者oligofren
相关产品推荐
相关产品推荐

