React Native:Reach-Navigation 和 Pouch-DB - db.put 在运行回调之前未完成"refresh"



相对新手;如果我在这里的礼节和形式不太好,请原谅我。我愿意接受反馈。

我使用create-rect本机应用程序创建了一个使用PouchDB(我相信它最终使用AsyncStorage)存储"项目"列表的应用程序(基本上)。

在TabNavigator(主应用程序)中,我有一个用于应用程序相关部分的StackNavigator("列表屏幕")。它查找DB并查询项,然后对每个返回的记录执行I.map()操作,以动态生成类似ListView的自定义组件。如果没有记录,它会交替显示一个提示,告诉用户这样做。在任何一种情况下,都有一个"添加项目"TouchableOpacity,将他们带到一个屏幕上,在那里他们可以添加一个新项目(他们将被带到"添加"屏幕)。

当从"添加"屏幕导航回来时,我使用的是SO上讨论过的一种模式,其中我传递了一个"刷新"函数作为导航参数。一旦用户使用"添加"屏幕上的按钮"保存"更改,它就会执行db.post()并添加它们,在"列表屏幕"上运行"刷新"功能,然后像这样导航回来:

<TouchableOpacity
style={styles.myButton}
onPress={() => {
if (this.state.itemBrand == '') {
Alert.alert(
'Missing Information',
'Please be sure to select a Brand',
[
{text: 'OK', onPress: () => 
console.log('OK pressed on AddItemScreen')},
],
{ cancelable: false }
)
} else {
this.createItem();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsScreen');
}
}
} 
>

所有这些都很好。"刷新"函数(作为onGoBack参数传递)运行良好。。。用于此屏幕。数据库与查询一起被调用,新条目被找到,项目的组件呈现得像一个符咒。

"列表屏幕"上呈现的每个类似ListItem的组件都包含一个带有"编辑"选项的react原生幻灯片。这些的onPress会将用户发送到"Item Details"屏幕,PouchDB中所选项目的_id会作为道具传递到"Item Details"屏幕,其中loadItem()componentDidMount中运行,并在数据库模块中执行db.get(id)。id(数组中的对象)的"events"属性列表中显示了其他详细信息,这些属性呈现为另一组类似ListItem的组件。

当选择将事件"添加"到项目的列表中时,就会出现问题。。。或删除它(通过这些项目的[另一个]滑出使用另一个功能。在从"添加事件"屏幕调用这两个功能中的任何一个后,有一个类似的向后导航,以与上面相同的形式调用,这就是"添加"示例:

async createEvent() {
var eventData = {
eventName: this.state.eventName.trim(),
eventSponsor: this.state.eventSponsor.trim(),
eventDate: this.state.eventDate,
eventJudge: this.state.eventJudge.trim(),
eventStandings: this.state.eventStandings.trim(),
eventPointsEarned: parseInt(this.state.eventPointsEarned.trim()),
};
var key = this.key;
var rev = this.rev;
await db.createEvent(key, rev, eventData);
}

它调用我的"db_ops"模块函数:

exports.createEvent = function (id, rev, eventData) { 
console.log('You called db.createEvent()');
db.get(id)
.then(function(doc) {
var arrWork = doc.events; //assign array of events to working variable
console.log('arrWork is first assigned: ' + arrWork);
arrWork.push(eventData);
console.log('then, arrWork was pushed and became: ' + arrWork);
var arrEvents = arrWork.sort((a,b)=>{
var dateA = new Date(a.eventDate), dateB = new Date(b.eventDate);
return b.eventDate - a.eventDate;
})
doc.events = arrEvents;
return db.put(doc);
})
.then((response) => {
console.log("db.createEvent() response was:n" + 
JSON.stringify(response));
})
.catch(function(err){
console.log("Error in db.createEvent():n" + err);
});
} 

之后,"添加事件"屏幕的按钮按与第一个类似的顺序触发以上内容,然后导航回:

this.createEvent();
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('ItemsDetails');

"刷新"功能看起来是这样的(在componentDidMount中也称为):

loadItem() {
console.log('Someone called loadItem() with this.itemID of ' + this.itemID);
var id = this.itemID;
let totalWon = 0;
db.loadItem(id)
.then((item) => {
console.log('[LOAD ITEM] got back data of:n' + JSON.stringify(item));
this.setState({objItem: item, events: item.events});
if (this.state.events.length != 0) { this.setState({itemLoaded: true});
this.state.events.map(function(event) { 
totalWon += parseInt(event.eventPointsEarned);
console.log('totalWon is ' + totalWon + ' with ' +
event.eventPointsEarned + ' having been added.');
});
};
this.setState({totalWon: totalWon});
})
.catch((err) => { 
console.log('db.loadItem() error: ' + err);
this.setState({itemLoaded: false});
});    
}

我不知道为什么当我添加项目时列表屏幕会刷新。。。但当我用PouchDB进行其他异步数据库操作时,我认为这与修改包含"事件"信息的对象,然后返回"项目详细信息"屏幕的方式类似。

我是不是在什么地方搞砸了Promise连锁店?深入导航时忽略StackNavigator的行为?

唯一的其他区别是,在非工作情况下,我在db函数中操作数组,而在其他情况下,在返回到上一屏幕上的更新状态之前,我只是创建/发布或删除/删除记录等。

编辑以添加,根据注释,返回到"列表屏幕",打开的"项目详细信息"确实会提取数据库数据,并正确显示更新已完成。

我所做的进一步检查还显示,createEvent()中用于打印对db调用的响应的console.log直到之后才记录,其他一些动态渲染方法在"项目详细信息"屏幕上被调用。因此,在createEvent()中的Promise链被解析之前,前一个屏幕似乎正在执行loadItem()调用的get()。更大的问题是否是由国家管理引起的,对我来说仍然不清楚——尽管这在某些方面是有意义的——因为无论我是否调用了onGoBack()函数,这种情况都可能发生。

Edit/bump:我已经尝试在db.get()上的db_ops模块和调用它的组件端loadItem()中的不同位置使用async/await。这些模块的时间安排有些不协调,我完全被困在这里了。除了尝试redux(在这种特殊情况下,我认为这太过分了),还有什么想法吗?

这与PDB或导航无关,而是关于如何管理依赖组件的外部更改(由于这些更改已在Navigator中安装,因此了解它们很重要,因此componentDidMount还不够)。如果你不使用类似全局状态冗余的管理(就像我一样),那么让它知道应该更新的依赖组件的唯一方法就是传递相应的道具并检查它们是否被更改。像这样:

//root.js
refreshEvents = ()=> { //pass it to DeleteView via screenProps
this.setState({time2refreshEvents: +new Date()}) //pass time2refreshEvents to EventList via screenProps
}
//DeleteView.js
//delete button... 
onPress={db.deleteThing(thingID).then(()=> this.props.screenProps.refreshEvents())}
//EventList.js
...
constructor(props) {
super(props);
this.state = {
events: [],
noEvents: false,
ready: false,
time2refreshEvents: this.props.screenProps.time2refreshEvents,
}
}
static getDerivedStateFromProps(nextProps, currentState) {
if (nextProps.screenProps.time2refreshEvents  !== currentState.time2refreshEvents ) {
return {time2refreshEvents : nextProps.screenProps.time2refreshEvents }
} else {
return null
}
}
componentDidMount() {
this._getEvents()
}
componentDidUpdate(prevProps, prevState) {
if (this.state.time2refreshEvents !== prevState.time2refreshEvents) {
this._getEvents()
}
}
_getEvents = ()=> {
//do stuff querying db and updating your list with actual data
}

最新更新