要构建我的react本机ListView,我需要从网络APi和AsyncStorage (比如AppCache)两个地方提取数据。来自AsyncStorage的数据可能存在,也可能不存在,但它需要以任何方式返回某些内容(例如,“未找到”)
以下是当前版本的要点,除了检索cachedOn date (第47行) https://gist.github.com/geirman/1901d4b1bfad42ec6d65#file-aircraftlist-js-L47外,我认为这是秘密酱汁的去处。
我认为这可能是任何ReactJS开发人员都可以回答的问题,尽管这个例子是针对本机特定的。

发布于 2016-11-12 17:57:37
这个问题似乎相当复杂,因为其中有多个级别的异步性:获取数据、读取/写入缓存和呈现列表行。在这种情况下,将问题分解为较小的组件通常会有所帮助。
我无法轻松地让示例代码运行,所以我使用了一个简化的示例。
首先,让我们将缓存封装到一个整洁的接口中,这样我们就不需要在使用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之类的状态管理库,以进一步简化代码--当然,它们也有自己的一组复杂性。
发布于 2016-11-11 06:03:41
这样做的简单方法是通过id mapping。
我可以看到,您的响应为每个项目提供了一个唯一的id。因此,在本地存储中存储基于相同ids的时间戳。当您映射来自api的结果项时,获取项目的id并将其传递到本地存储的getItem()。这将返回id的时间。
const randomTime = [{
id: 1,
date: '05-Jun-2032 14:37:11'
}, {
id: 2,
date: '30-Jun-2006 00:02:27'
}, {
id: 4,
date: '22-Aug-1996 02:47:28'
}, {
id: 6,
date: '04-Jan-1991 23:27:15'
}]
const preProcessLocalStorage = () => {
const data = JSON.parse(localStorage.getItem('date')) //read data from local storage
const obj = {}
data.forEach((el) => {
obj[el.id] = el //convert into ids as keys object for better data retrieval
})
return obj
}
class App extends React.Component{
constructor(props){
super(props)
this.state = {
loading: true,
}
this.apiData = []
this.localData = []
localStorage.setItem('date', JSON.stringify(randomTime)) //set Local data
}
componentDidMount() {
this.localData = preProcessLocalStorage()
$.get('https://jsonplaceholder.typicode.com/posts')
.done((data) => {
this.apiData = data
this.setState({loading: false})
})
}
render(){
if(this.state.loading) return false
const list = this.apiData.map((el) => {
const time = this.localData[el.id] //find local data based on the api data id
return <div>
<h1>{el.id} - {el.title}</h1>
<h4>{time || '-'}</h4>
</div>
})
return <div>{list}</div>
}
}
ReactDOM.render(<App/>, document.getElementById('app'))https://stackoverflow.com/questions/33424397
复制相似问题