2015-10-29 21 views
3

要構建我自己的反應原生ListView,我需要從網絡APi和AsyncStorage(如AppCache)兩個位置提取數據。從AsyncStorage數據可能會或可能不會在那裏,但它需要返回的東西無論哪種方式(例如,「未找到」)使用API​​和AsyncStorage數據構建ListView

下面是當前版本的要點,除了檢索cachedOn日期其中工程(Line 47https://gist.github.com/geirman/1901d4b1bfad42ec6d65#file-aircraftlist-js-L47,這是我相信祕密醬油的地方。

我認爲這可能是任何ReactJS開發者可能回答的問題,儘管這個例子是React Native特有的。

Screenshot

+0

你有沒有想出一個解決方案呢?我正試圖解決同樣的問題。 – Jonovono

+0

是的,但我從未對解決方案感到滿意。您可以在github上看到我的代碼https://github.com/geirman/RepairMaps/感謝您再次關注此問題。看起來有幾個新的答案來審查! –

回答

2

這個問題似乎很複雜,因爲在異步玩多層次的:獲取數據,讀/寫高速緩存,以及渲染列表行。在這種情況下,將問題分解爲更小的組件通常會有所幫助。

我無法輕鬆獲取示例代碼,因此我正在使用一個簡化示例。

首先,讓我們結束緩存成一個整潔的界面,所以我們不需要去想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)以進一步簡化代碼 - 當然,它們自帶一套複雜性。

0

這樣做的簡單方式是通過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'))