這個問題似乎很複雜,因爲在異步玩多層次的:獲取數據,讀/寫高速緩存,以及渲染列表行。在這種情況下,將問題分解爲更小的組件通常會有所幫助。
我無法輕鬆獲取示例代碼,因此我正在使用一個簡化示例。
首先,讓我們結束緩存成一個整潔的界面,所以我們不需要去想AsyncStorage
語義,而與它一起工作:
const aircraftCache = {
// returns promise of cached aircraft, or null if not found
getAircraft(aircraftId) {
return AsyncStorage.getItem(aircraftId).then(data => (
data ? JSON.parse(data) : null
));
},
// caches given aircraft object with a fresh cachedOn date
// and returns a promise of the cached aircraft
setAircraft(aircraftId, aircraft) {
const cached = {...aircraft, cachedOn: new Date()};
return AsyncStorage.setItem(aircraftId, JSON.stringify(cached)).then(() => cached);
},
// clears given aircraft from cache and return Promise<null>
clearAircraft(aircraftId) {
return AsyncStorage.removeItem(aircraftId).then(() => null);
}
}
那麼,讓我們來限制AircraftList
責任只是顯示數據的列表中,裝載指示等,並提取行渲染到一個單獨的組件:
class AircraftList extends Component {
static propTypes = {
aircraft_list: PropTypes.arrayOf(PropTypes.shape({
reg_number: PropTypes.string,
ti_count: PropTypes.number
}))
}
constructor(props) {
super(props);
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
dataSource: this.ds.cloneWithRows(this.props.aircraft_list),
isLoading: false,
showingCache: false
};
}
aircraftLoaded(aircraft) {
this.setState({isLoading: false});
this.props.navigator.push({
title: 'TI Lookup',
component: TrackedItemIndex,
passProps: {aircraft_object: aircraft}
});
}
renderRow(aircraft) {
return (
<AircraftRow
reg_number={aircraft.reg_number}
ti_count={aircraft.ti_count}
loading={() => this.setState({isLoading: true})}
loaded={this.aircraftLoaded.bind(this)}
/>
);
}
render() {
// simplified view
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
/>
);
}
}
各個行渲染,提取和然後緩存操作可被封裝到AircraftRow
組件:
class AircraftRow extends Component {
static propTypes = {
reg_number: PropTypes.string,
ti_count: PropTypes.number,
loading: PropTypes.func,
loaded: PropTypes.func
}
state = { cachedOn: null };
constructor(props) {
super(props);
this.loadDetails = this.loadDetails.bind(this);
this.clearDetails = this.clearDetails.bind(this);
this.setCachedOn = this.setCachedOn.bind(this);
}
componentWillMount() {
// when component is loaded, look up the cached details and
// set the cachedOn timestamp into state
aircraftCache.getAircraft(this.props.reg_number).then(this.setCachedOn);
}
loadDetails() {
const id = this.props.reg_number;
// notify parent that loading has started
if (this.props.loading) {
this.props.loading(id);
}
// fetch and cache the data
this.fetchDetails(id)
.then((aircraft) => {
// notify parent that loading has finished
if (this.props.loaded) {
this.props.loaded(aircraft);
}
})
.catch((e) => {
console.error(e);
});
}
fetchDetails(id) {
// get details from the api, and fall back to the cached copy
return Api.getTrackedItems(id)
.then(aircraft => aircraftCache.setAircraft(id, aircraft))
.then(this.setCachedOn)
.catch(() => aircraftCache.getAircraft(id));
}
clearDetails() {
// clear item from cache and update local state with null aircraft
const id = this.props.reg_number;
aircraftCache.clearAircraft(id).then(this.setCachedOn);
}
setCachedOn(aircraft) {
// update local state (aircraft can be null)
this.setState({ cachedOn: aircraft ? aircraft.cachedOn.toString() : null })
return aircraft;
}
render() {
// simplified view
return (
<View>
<Text>{this.props.reg_number}</Text>
<Text>{this.props.ti_count}</Text>
<Text>{this.state.cachedOn}</Text>
<Text onPress={this.loadDetails}>Load details</Text>
<Text onPress={this.clearDetails}>Clear details</Text>
</View>
)
}
}
對於我的錢,這個觀點仍然太多了。我建議您查看狀態管理庫(如Redux或MobX)以進一步簡化代碼 - 當然,它們自帶一套複雜性。
你有沒有想出一個解決方案呢?我正試圖解決同樣的問題。 – Jonovono
是的,但我從未對解決方案感到滿意。您可以在github上看到我的代碼https://github.com/geirman/RepairMaps/感謝您再次關注此問題。看起來有幾個新的答案來審查! –