【发布时间】:2018-05-25 23:18:47
【问题描述】:
我是 React Native 的新手。
目前我有一个简单的 React Native 应用程序。我想使用带有 redux-saga 的 API 获取数据。
我已经配置了 redux-saga,但是 API 配置出了点问题(我猜)。
以下是我修改的一些文件:
ZomatoAPI.js
import apisauce from 'apisauce'
const create = (baseURL = 'https://developers.zomato.com/api/v2.1/') => {
const api = apisauce.create({
baseURL,
headers:{
'Cache-Control': 'no-cache',
'user-key': '0f929cccab4622bf301895bc0d9dd415'
},
// 10 sec
timeout: 10000
})
const getCategories = () => api.get('categories')
return{
getCategories
}
}
ReactRestoSagas.js
import { call, put } from 'redux-saga/effects'
import { path } from 'ramda'
import ReacRestoActions from '../Redux/ReactRestoRedux'
//import ZomatoAPI from '../Services/ZomatoAPI'
//const api = ReactRestoAPI.create()
export function* getCategories(api){
const response = yield call(api.getCategories)
if(response.ok){
//ok, call action from redux
yield put(ReacRestoActions.categoriesSucceed(payload))
}else{
yield put(ReacRestoActions.categoriesFailed())
}
}
index.js (Sagas)
import { takeLatest, all } from 'redux-saga/effects'
//import API from '../Services/Api'
import FixtureAPI from '../Services/FixtureApi'
import DebugConfig from '../Config/DebugConfig'
import ZomatoAPI from '../Services/ZomatoAPI'
/* ------------- Types ------------- */
import { StartupTypes } from '../Redux/StartupRedux'
import { GithubTypes } from '../Redux/GithubRedux'
import { ReactRestoTypes } from '../Redux/ReactRestoRedux'
/* ------------- Sagas ------------- */
import { startup } from './StartupSagas'
import { getUserAvatar } from './GithubSagas'
import { getCategories } from './ReactRestoSagas'
/* ------------- API ------------- */
// The API we use is only used from Sagas, so we create it here and pass along
// to the sagas which need it.
const api = DebugConfig.useFixtures ? FixtureAPI : ZomatoAPI.create()
/* ------------- Connect Types To Sagas ------------- */
export default function * root () {
yield all([
// some sagas only receive an action
//takeLatest(StartupTypes.STARTUP, startup),
// some sagas receive extra parameters in addition to an action
//takeLatest(GithubTypes.USER_REQUEST, getUserAvatar, api),
takeLatest(ReactRestoTypes.CATEGORIES_REQUEST, getCategories, api)
])
}
ReactRestoRedux.js
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
// Categories types
const { Types, Creators } = createActions({
categoriesRequest: null,
categoriesSucceed: ['payload'],
categoriesFailed: null
})
export const ReactRestoTypes = Types
export default Creators
// Initial state
export const INITIAL_STATE = Immutable({
payload: null,
errorMessage: null,
fetchCategories: false
})
// Reducers
export const categoriesRequest = (state) =>
state.merge({ fetchCategories: true })
export const categoriesSucceed = (state, action) => {
const { payload } = action
return state.merge({ fetchCategories: false, errorMessage: null, payload})
}
export const categoriesFailed = (state) =>
state.merge({ fetchCategories: false, errorMessage: true})
export const reducer = createReducer(INITIAL_STATE, {
[Types.CATEGORIES_REQUEST]: categoriesRequest,
[Types.CATEGORIES_SUCCEED]: categoriesSucceed,
[Types.CATEGORIES_FAILED]: categoriesFailed
})
index.js (Redux)
import React, { Component } from 'react'
import { Picker, TouchableOpacity, View, ListView, Text } from 'react-native'
import { connect } from 'react-redux'
import { Icon, Header } from 'react-native-elements'
import { Button, Container, Content, Footer, Title} from 'native-base'
// For empty lists
// import AlertMessage from '../Components/AlertMessage'
// Styles
import styles from './Styles/MainRestoStyles'
class MainRestoScreen extends React.Component {
constructor (props) {
super(props)
/*
const dataObjects = []
{
first: [
{title: 'First Title', description: 'First Description'},
{title: 'Second Title', description: 'Second Description'},
{title: 'Third Title', description: 'Third Description'},
{title: 'Fourth Title', description: 'Fourth Description'},
{title: 'Fifth Title', description: 'Fifth Description'},
{title: 'Sixth Title', description: 'Sixth Description'},
{title: 'Seventh Title', description: 'Seventh Description'},
{title: 'Eighth Title', description: 'Eighth Description'},
{title: 'Ninth Title', description: 'Ninth Description'},
{title: 'Tenth Title', description: 'Tenth Description'}
]
}
*/
const rowHasChanged = (r1, r2) => r1 !== r2
const sectionHeaderHasChanged = (s1, s2) => s1 !== s2
// DataSource configured
const ds = new ListView.DataSource({rowHasChanged, sectionHeaderHasChanged})
// Datasource is always in state
this.state = {
dataObjects: [],
dataSource: ds.cloneWithRowsAndSections([])
}
this.stateCity = { city: "ny" }
}
prepareCategories(){
//check is there any data
if(!this.props.payload){
//no data, call api
this.props.getCategories()
}else{
this.setState({
dataObjects: this.props.payload.dataObjects,
dataSource: this.state.dataSource(cloneWithRowsAndSections(this.props.dataObjects))
})
}
}
checkCategories(newProps){
if(newProps.payload){
this.setState({
dataObjects: newProps.payload.dataObjects,
dataSource: this.state.dataSource.cloneWithRowsAndSections(newProps.payload.dataObjects)
})
}
}
componentWillMount(){
this.prepareCategories()
}
componentWillReceiveProps(newProps){
this.checkCategories(newProps)
}
renderRow (rowData, sectionID) {
// You can condition on sectionID (key as string), for different cells
// in different sections
return (
<TouchableOpacity style={styles.row}>
<Text style={styles.boldLabel}>Section {sectionID} - {rowData.title}</Text>
<Text style={styles.label}>{rowData.description}</Text>
</TouchableOpacity>
)
}
/* ***********************************************************
* STEP 4
* If your datasource is driven by Redux, you'll need to
* reset it when new data arrives.
* DO NOT! place `cloneWithRowsAndSections` inside of render, since render
* is called very often, and should remain fast! Just replace
* state's datasource on newProps.
*
* e.g.
componentWillReceiveProps (newProps) {
if (newProps.someData) {
this.setState(prevState => ({
dataSource: prevState.dataSource.cloneWithRowsAndSections(newProps.someData)
}))
}
}
*************************************************************/
// Used for friendly AlertMessage
// returns true if the dataSource is empty
noRowData () {
return this.state.dataSource.getRowCount() === 0
}
render () {
return (
<Container>
<View style={styles.toolbar}>
<Text style={styles.toolbarButton}></Text>
<Icon name='bowl' type='entypo' size={40} color='white'/>
<Picker
style={styles.dropdown}
selectedValue={this.state.city}
onValueChange={(city) => this.setState({city})}>
<Picker.Item label="New York City" value="ny" />
<Picker.Item label="New Jersey" value="nj" />
<Picker.Item label="Los Angeles" value="la" />
<Picker.Item label="Oklahoma City" value="oc" />
</Picker>
</View>
<Content>
<ListView
contentContainerStyle={styles.listContent}
dataSource={this.state.dataSource}
onLayout={this.onLayout}
renderRow={this.renderRow}
enableEmptySections
/>
</Content>
<Footer style={ styles.bars }>
<Button transparent style={ styles.buttonsMenu }>
<Icon name='location' type='entypo' color='white' size={30}/>
</Button>
<Button transparent style={ styles.buttonsMenu }>
<Icon name='heart' type='foundation' color='white' size={30}/>
</Button>
<Button transparent style={ styles.buttonsMenu }>
<Icon name='bell' type='entypo' color='white' size={30}/>
</Button>
</Footer>
</Container>
)
}
}
//MainRestoScreen.propsTypes = {}
const mapStateToProps = (state) => {
return {
// ...redux state to props here
payload: state.reactResto.payload,
errorMessage: state.reactResto.errorMessage,
fetchCategories: state.reactResto.fetchCategories
}
}
const mapDispatchToProps = (dispatch) => {
return {
getCategories: () => dispatch(ReactRestoActions.getCategories())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MainRestoScreen)
MainRestoScreen.js
import React, { Component } from 'react'
import { Picker, TouchableOpacity, View, ListView, Text } from 'react-native'
import { connect } from 'react-redux'
import { Icon, Header } from 'react-native-elements'
import { Button, Container, Content, Footer, Title} from 'native-base'
// For empty lists
// import AlertMessage from '../Components/AlertMessage'
// Styles
import styles from './Styles/MainRestoStyles'
class MainRestoScreen extends React.Component {
constructor (props) {
super(props)
/*
const dataObjects = []
{
first: [
{title: 'First Title', description: 'First Description'},
{title: 'Second Title', description: 'Second Description'},
{title: 'Third Title', description: 'Third Description'},
{title: 'Fourth Title', description: 'Fourth Description'},
{title: 'Fifth Title', description: 'Fifth Description'},
{title: 'Sixth Title', description: 'Sixth Description'},
{title: 'Seventh Title', description: 'Seventh Description'},
{title: 'Eighth Title', description: 'Eighth Description'},
{title: 'Ninth Title', description: 'Ninth Description'},
{title: 'Tenth Title', description: 'Tenth Description'}
]
}
*/
const rowHasChanged = (r1, r2) => r1 !== r2
const sectionHeaderHasChanged = (s1, s2) => s1 !== s2
// DataSource configured
const ds = new ListView.DataSource({rowHasChanged, sectionHeaderHasChanged})
// Datasource is always in state
this.state = {
dataObjects: [],
dataSource: ds.cloneWithRowsAndSections([])
}
this.stateCity = { city: "ny" }
}
prepareCategories(){
//check is there any data
if(!this.props.payload){
//no data, call api
this.props.getCategories()
}else{
this.setState({
dataObjects: this.props.payload.dataObjects,
dataSource: this.state.dataSource(cloneWithRowsAndSections(this.props.dataObjects))
})
}
}
checkCategories(newProps){
if(newProps.payload){
this.setState({
dataObjects: newProps.payload.dataObjects,
dataSource: this.state.dataSource.cloneWithRowsAndSections(newProps.payload.dataObjects)
})
}
}
componentWillMount(){
this.prepareCategories()
}
componentWillReceiveProps(newProps){
this.checkCategories(newProps)
}
renderRow (rowData, sectionID) {
// You can condition on sectionID (key as string), for different cells
// in different sections
return (
<TouchableOpacity style={styles.row}>
<Text style={styles.boldLabel}>Section {sectionID} - {rowData.title}</Text>
<Text style={styles.label}>{rowData.description}</Text>
</TouchableOpacity>
)
}
/* ***********************************************************
* STEP 4
* If your datasource is driven by Redux, you'll need to
* reset it when new data arrives.
* DO NOT! place `cloneWithRowsAndSections` inside of render, since render
* is called very often, and should remain fast! Just replace
* state's datasource on newProps.
*
* e.g.
componentWillReceiveProps (newProps) {
if (newProps.someData) {
this.setState(prevState => ({
dataSource: prevState.dataSource.cloneWithRowsAndSections(newProps.someData)
}))
}
}
*************************************************************/
// Used for friendly AlertMessage
// returns true if the dataSource is empty
noRowData () {
return this.state.dataSource.getRowCount() === 0
}
render () {
return (
<Container>
<View style={styles.toolbar}>
<Text style={styles.toolbarButton}></Text>
<Icon name='bowl' type='entypo' size={40} color='white'/>
<Picker
style={styles.dropdown}
selectedValue={this.state.city}
onValueChange={(city) => this.setState({city})}>
<Picker.Item label="New York City" value="ny" />
<Picker.Item label="New Jersey" value="nj" />
<Picker.Item label="Los Angeles" value="la" />
<Picker.Item label="Oklahoma City" value="oc" />
</Picker>
</View>
<Content>
<ListView
contentContainerStyle={styles.listContent}
dataSource={this.state.dataSource}
onLayout={this.onLayout}
renderRow={this.renderRow}
enableEmptySections
/>
</Content>
<Footer style={ styles.bars }>
<Button transparent style={ styles.buttonsMenu }>
<Icon name='location' type='entypo' color='white' size={30}/>
</Button>
<Button transparent style={ styles.buttonsMenu }>
<Icon name='heart' type='foundation' color='white' size={30}/>
</Button>
<Button transparent style={ styles.buttonsMenu }>
<Icon name='bell' type='entypo' color='white' size={30}/>
</Button>
</Footer>
</Container>
)
}
}
//MainRestoScreen.propsTypes = {}
const mapStateToProps = (state) => {
return {
// ...redux state to props here
payload: state.reactResto.payload,
errorMessage: state.reactResto.errorMessage,
fetchCategories: state.reactResto.fetchCategories
}
}
const mapDispatchToProps = (dispatch) => {
return {
getCategories: () => dispatch(ReactRestoActions.getCategories())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MainRestoScreen)
我正在寻求帮助来解决这个问题。在这里呆了几个小时。
【问题讨论】:
标签: android api react-native redux-saga