React Native中‘无法在已卸载组件更新状态’错误修复求助
解决React Native中SQLite异步操作后更新卸载组件状态的警告
先梳理你代码里的几个核心问题,这些正是导致警告和_isMounted标志无效的原因:
1. setState的第二个参数使用错误
你这段代码的写法有问题:
this.setState({years: rows}, {loadingData: false});
setState的第二个参数是回调函数,不是状态对象。所以loadingData: false根本没生效,你的loadingData会一直保持true——这不仅会让组件一直卡在加载状态,还会导致getYears重复执行(因为你判断了if(this.state.loadingData)),进一步加剧状态更新的问题。
正确写法应该把loadingData放到第一个参数的状态对象里:
this.setState({ years: rows, loadingData: false });
2. _isMounted标志没有在异步回调中检查
SQLite事务是异步操作,当组件卸载后,这个回调仍然会执行,但你没有在回调里检查_isMounted的状态,所以还是会触发setState,导致警告。
修改getYears,在执行setState前先判断组件是否还挂载:
getYears = () => { if (this.state.loadingData) { this.db.transaction((txn) => { txn.executeSql( "SELECT cod, slug FROM year", [], (tx, res) => { // 先检查组件是否仍挂载,已卸载则直接返回 if (!this._isMounted) return; if (res.rows.length > 0) { // 声明rows为局部变量,避免全局污染 let rows = []; for (let i = 0; i < res.rows.length; i++) { rows.push(res.rows.item(i)); } this.setState({ years: rows, loadingData: false }); } else { // 没有数据的情况也要更新loading状态 this.setState({ loadingData: false }); } } ); }); } };
3. 组件卸载时标记_isMounted为false
必须在componentWillUnmount里更新标志,确保异步回调能感知到组件已卸载:
componentWillUnmount() { this._isMounted = false; }
4. render方法的else分支缺少return
你当前的else分支只是写了<Text> Loading... </Text>,但没有return,会导致组件返回undefined,这是语法错误,需要补上:
} else { return <Text> Loading... </Text>; }
5. 额外修复:Picker.Item的map函数缺少return
你的pickerItens生成逻辑里,map函数没有返回组件,导致生成的数组全是undefined,Picker里不会显示任何选项,这个也一起修正:
const pickerItens = state.years.map((record, index) => { return <Picker.Item key={index} label={record.slug} value={record.cod}/> })
完整修正后的代码片段
_isMounted = false; constructor(props) { super(props); this.state = { slug: "", value: "", cod_year: "", months: [], years: [], loadingData: true, }; } db = openDatabase({ name: 'test.db', createFromLocation : '~my_finances.db'}); getYears = () => { if(this.state.loadingData){ this.db.transaction((txn)=>{ txn.executeSql( "SELECT cod, slug FROM year", [], (tx, res) => { if(!this._isMounted) return; if(res.rows.length > 0){ let rows = []; for(let i=0; i<res.rows.length;i++){ rows.push(res.rows.item(i)); } this.setState({years: rows, loadingData: false}); } else { this.setState({loadingData: false}); } } ) }) } } componentDidMount(){ this._isMounted = true; this.getYears(); } componentWillUnmount() { this._isMounted = false; } render() { const state = this.state; if(!state.loadingData){ const pickerItens = state.years.map((record, index) => { return <Picker.Item key={index} label={record.slug} value={record.cod}/> }) return ( <View style={{flex: 1}} > <ScrollView style={styles.container}> <View style={styles.form_add}> <Text style={styles.form_slug} > Adicionar </Text> <TextInput placeholder="Digite o mês..." onChangeText={(slug) => { this.setState({slug}) }} value={this.state.slug} style={styles.input_add} /> <TextInput placeholder="Digite o valor..." onChangeText={(value) => { this.setState({value}) }} value={this.state.value} style={styles.input_add} /> <Picker onValueChange = {(cod_year) => {this.setState({cod_year})}}> { pickerItens } </Picker> <Button color="#7159C1" title="Salvar" onPress={() => this.addYear()} /> </View> </ScrollView> <Footer navigate={this.props.navigation.navigate} /> </View> ); }else{ return <Text> Loading... </Text> } }
这些修改应该能彻底解决状态更新的警告,同时修复代码里的其他隐性问题。
内容的提问来源于stack exchange,提问作者Showtime
相关产品推荐
相关产品推荐

