0
votes

I'm developing an app using React Native and redux. Express js for the Ajax requests.

The data from my Ajax request won't load and here is the warning I'm getting:

Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.

Not entirely sure what I'm doing wrong?

index.ios.js

import React, { Component } from 'react'

import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  NavigatorIOS
} from 'react-native'

import { Provider } from 'react-redux'
import store from './src/store'

import CategoryView from './src/category-view'

class myApp extends Component {
  render() {
    return (
      <Provider store={store}>
        <NavigatorIOS
          initialRoute={{
            component: CategoryView,
            title: 'myApp',
            index: 0
          }}
        />
      </Provider>
    );
  }
}

store.js

import { combineReducers, createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import category from './reducers/category'
import listings from './reducers/listings'

const reducers = combineReducers({
  category,
  listings
})

const initialState = {}

const store = createStore(
  reducers,
  initialState,
  applyMiddleware(
    thunkMiddleware
  )
)

export default store

Here is my view file:

import React from 'react'
import {
  View,
  ListView,
  ScrollView,
  StyleSheet,
  Image,
  TouchableHighlight
} from 'react-native'

import { connect } from 'react-redux'
import Listings from './listings'
import BetView from './bet-view'
import { getListings } from './actions/listings'

const getDataSource = (listings) =>
  (new ListView.DataSource({
    rowHasChanged: (r1, r2) => r1 != r2
  })).cloneWithRows(listings)

const _ListingsView = ({dataSource, navigator, onPress, onLoad}) => {
  onLoad()
  return(
    <ScrollView>
      <ListView
        dataSource={dataSource}
        renderRow={(row) =>
          <Listings
            teamOne={row.game[0]}
            teamTwo={row.game[1]}
            date={row.date}
            onPress={() => onPress(navigator, row.name)}
          />
        }
      />
    </ScrollView>
  )
}

const ListingsView = connect(
  (state) => {
    return {
      dataSource: getDataSource(state.listings.items || []),
      loading: state.listings.loading
    }
  },
  (dispatch) => {
    return {
      onPress: (navigator, name) =>
        navigator.push({
          title: 'test',
          component: BetView,
          passProps: {
            category: name
          }
        }),
      onLoad: () => dispatch(getListings())
    }
  }
)(_ListingsView)

export default ListingsView

Action file:

const URL = 'http://localhost:3000/listings'

export const REQUEST_LISTINGS = 'request-listings'
export const RECEIVE_LISTINGS = 'receive-listings'

const requestListings = () =>
({
  type: REQUEST_LISTINGS
})

const receiveListings = (items) =>
({
  type: RECEIVE_LISTINGS,
  items: items || []
})

const shouldFetch = (state) => {
  const res = (!state.listings.items || 0 == state.listings.items.length && !state.listings.loading)
  return res
}

export const getListings = () =>
  (dispatch, getState) =>
    shouldFetch(getState())
    && dispatch(requestListings())
    && fetch(URL)
      .then(res => res.json())
      .then(json => dispatch(receiveListings(json)))

Here is the reducer:

import { RECEIVE_LISTINGS, REQUEST_LISTINGS } from '../actions/listings'

export default (state = {}, action) => {
  switch (action.type) {
    case RECEIVE_LISTINGS:
      return {
        loading: false,
        items: action.items
      }
    case REQUEST_LISTINGS:
      return {
        loading: true,
        items: []
      }
  }
  return state
}
1
Are there any components in your app that use local state? In other words, are you calling setState anywhere?Max Sindwani
This error usually happens when you try to update the state in the render function. React is declarative, so render should not manipulate the state.Max Sindwani
Could be cause you are using a stateless component and everything happens during the render method. With a class base component you could wire the getDataSource inside componentWillMountKevin Amiranoff
You are calling onLoad() in render() method of _ListingsView. Avoid this using component life cycle methods like componentWillMountJyothi Babu Araja
I've just updated with my question and added the index.ios.js showing the render methodJohn

1 Answers

0
votes

you could try something like this. I have not tested it. Not sure it is perfect. but that is the idea

import React, {Component} from 'react'
import {
  View,
  ListView,
  ScrollView,
  StyleSheet,
  Image,
  TouchableHighlight
} from 'react-native'

import { connect } from 'react-redux'
import Listings from './listings'
import BetView from './bet-view'
import { getListings } from './actions/listings'

const getDataSource = (listings) =>
  (new ListView.DataSource({
    rowHasChanged: (r1, r2) => r1 != r2
  })).cloneWithRows(listings)

class _ListingsView extends Component {


  componentWillMount() {
    this.props.onLoad();
  }

  render() {
    return(
      <ScrollView>
        <ListView
          dataSource={this.props.dataSource}
          renderRow={(row) =>
            <Listings
              teamOne={row.game[0]}
              teamTwo={row.game[1]}
              date={row.date}
              onPress={() => this.props.onPress(this.props.navigator, row.name)}
            />
          }
        />
      </ScrollView>
    )
  }
}

const ListingsView = connect(
  (state) => {
    return {
      dataSource: getDataSource(state.listings.items || []),
      loading: state.listings.loading
    }
  },
  (dispatch) => {
    return {
      onPress: (navigator, name) =>
        navigator.push({
          title: 'test',
          component: BetView,
          passProps: {
            category: name
          }
        }),
      onLoad: () => dispatch(getListings())
    }
  }
)(_ListingsView)

export default ListingsView

So you would be better following their example : https://facebook.github.io/react-native/docs/listview.html

Or this is how I manage ListViews in the application i am currently building: https://github.com/kamiranoff/mythology/blob/master/src/components/HeroesList/HeroesList.js (In that example it is a little different because the call to the api is made even before this component is render and on filtering the list, that is why I update the state of datasource in componentWillReceiveProps )