React Native implement JSON data on ListView - react-native

I'm having a problem with implementing API data in ListView. I fetched JSON using Axios.
export function fetchRateService() {
return function(dispatch) {
axios.get(RATE_URL)
.then(response => {
dispatch({
type: FETCH_RATE_SERVICE,
payload: response.data
});
})
.catch((error) => {
console.log(error);
})
}
}
Reducer. I added rates data into array
import {
FETCH_RATE_SERVICE
} from '../actions/types';
const INITIAL_STATE = {
base: '',
date: '',
rates: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_RATE_SERVICE:
return {
...state,
base: action.payload.base,
date: action.payload.date,
rates: [ ...state.rates, action.payload.rates ]
};
default:
return state;
}
};
This is the component
class ConturyList extends Component {
componentWillMount() {
this.props.fetchRateService();
this.createDataSource(this.props);
}
createDataSource({rates}) {
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.dataSource = ds.cloneWithRows(rates);
}
renderRow(rate) {
return <ListItem rate={rate} />
};
render() {
console.log(this.props);
const { CardSectionStyle, textStyle, containerStyle } = styles;
const { visible, closeModal } = this.props;
return (
<Modal
visible={visible}
transparent={false}
animationType="slide"
onRequestClose={() => {this.props.closeModal()}}
>
<ListView
enableEmptySections
dataSource={this.dataSource}
renderRow={this.renderRow}
/>
</Modal>
);
}
}
const mapStateToProps = state => {
return {
rates: state.rateService.rates,
date: state.rateService.date,
base: state.rateService.base
};
}
export default connect(mapStateToProps, { fetchRateService } )(ConturyList);
The problem is I can see the props data using console.log(this.props);
enter image description here
I'm spending more than 3 days to figure out why this is not working. I tried using map() adding on
renderRow(rate) {
return rate.map((data) => {
return <ListItem rate={data} />
};
but it did not work. All the conutry code is in one object, Do I need to split the data by commas?.
Appreciate you helps. Thank you
UPDATE
So I'm trying to implement FlatList instead using ListView. The problem is on the JSON data. enter image description here. I want to implement key which are CountryCurrencyCode(AUD, JPN, etc..) to FlatList. Since rates is an object within an object, I added rates object into an array(reducer). But this.props.rates[0] can't be implemented on data property of FlatList. What kind of method can I try? I can't think of anything. I could print out key using map() when rates is object and then I can't implement it on the FlatList.

I would recommend switching over to the new FlatList component over ListView. FlatList just accepts an array of data to hydrate.
Initiate this.state.datasource as an empty array
constructor(props) {
super(props);
this.state = {
dataSource: [],
}
}
Fetch your data and hydrate this.state.dataSource from your Redux reducer/action
ComponentDidMount(){
this.props.fetchRateService();
var myData = this.props.rates[0];
this.setState({
dataSource:myData
)}
}
Now that your this.state.dataSource is set, we can populate FlatList
<FlatList
data={this.state.dataSource}
renderItem={({item})=>this.renderRow(item)}
/>
Flat List will throw a warning about a key extractor
Add this line below to the FlatList component. You will need to change 'item.key' to fit your own unique child. You can just keep it out for now for development.
keyExtractor={item => item.key}
You should see your data now! Keep in mind, you don't have to set the this.state.dataSource. Its just how I do it. You can plug 'this.props.rates' array directly into FlatList instead. Check out the FlatList docs for all the different things you can do with it. Hope this helps!

Related

Keep getting undefined when i add item with redux

need some serious help here. I am currently using a class component to add my items to cart but it looks like i keep getting undefined, can someone point me in the right direction please?
Homepage :
const mapStateToProps = state => ({
cartItems: state.cart.cartItems
});
const mapDispatchToProps = dispatch => {
return {
addToCartHandler: item => {
dispatch(addToCart(item))
}
}
}
renderRow =({item}) =>{
console.log(this.state.cartItems)
return(
<TouchableHighlight >
<View style ={styles.card}>
<Image source={{uri:item.image}} style={styles.image} />
<Icon name="add" size ={20} style= {styles.addToCartBtn} onPress ={() => this.props.addToCartHandler(item)} />
</TouchableHighlight>
)
}
reducers
import { ADD_TO_CART } from '../constants'
const initialState = {
cartItems: [],
totalPrice :0
}
//add item
export const cart = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
console.log("reducer",action)
var {item} =action.payload;
var newState =Object.assign({},{...state});
for (var i = 0; i < state.cartItems.length; i++) {
newState.cartItems = newState.cartItems.concat(item);
console.log(newState)
return newState
}
default:
return state
}
}
action
import {ADD_TO_CART} from '../constants'
import {REMOVE_FROM_CART} from '../constants'
export const addToCart =(item) =>{
// console.log(item)
return{
type: ADD_TO_CART,
payload : item,
};
}
Dont think anything is wrong with my action or rootreducers. I am not sure why i keep getting undefined whenever i add to cart when i can console.log the item
You are not creating the new state correctly, I think the following will work:
case ADD_TO_CART:
console.log('reducer', action);
var item = action.payload;
return {
...state,
cartItems:state.cartItems.concat(item)
}
Here is some useful information on how to create a new state.

Redux TypeError: Invalid attempt to spread non-iterable instance. In order to be iterable, non-array objects must have a [Symbol.iterator]() method

I am a beginner integrating redux with my code but i am facing this error when i select a tag from auto tags it gives the error non-iterable instance. The deletion reducer works fine after testing by giving redux empty state a [{name:'ABC'}]
so please guide me in figuring out the issue with my addtags redux function.
First screen:
class Page2 extends React.Component {
componentDidMount(){
this.props.fetchTags();
}
handleSubmit = () => {
addItem(this.props.tags.currentTags);
ToastAndroid.show('Symptoms saved successfully', ToastAndroid.SHORT)
};
handleDelete = index => {
this.props.deleteTags(index)
}
handleAddition = suggestion => {
this.props.addTags(suggestion)
console.log(this.props.tags.currentTags)
}
render() {
return (
<View style={styles.container}>
<View style={styles.autocompleteContainer}>
<AutoTags // Text adding component with auto completion feature and bubble feature
suggestions={this.props.tags.storedTags}
tagsSelected={this.props.tags.currentTags}
handleAddition={this.handleAddition}
handleDelete={this.handleDelete}
placeholder="Add a Symptom.." />
</View>
<TouchableHighlight // wrapper for making views respond properly to touches
style={styles.button}
underlayColor="blue"
onPress={() => {this.props.navigation.navigate('Diagnosis'); this.handleSubmit();}}>
<Text style={styles.buttonText}>Search</Text>
</TouchableHighlight>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
tags: state.tags,
}
}
const mapDispatchToProps = {
addTags,
deleteTags,
fetchTags
}
export default connect(mapStateToProps, mapDispatchToProps)(Page2);
Reducer:
import {ADD_TAGS,DELETE_TAGS , FETCH_TAGS,QUERY_RESULT } from '../actions/tags/tagsActionTypes'
import firebase from '../../config';
const db= firebase.firestore();// connect with firestore of firebase
let itemsRef = db.collection('datasetSymptom');
const initialState = {
currentTags: [],
storedTags:[],
result:[]
}
const tagsReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TAGS:
return {
...state,
currentTags: [...state.currentTags, action.payload]
};
break
case DELETE_TAGS:
const newArray = [...state.currentTags] //Copying state array
newArray.splice(action.payload,1);
//using splice to insert at an index
return {
...state,
currentTags: newArray //reassigning todos array to new array
}
break
case FETCH_TAGS:
const storedTags=[];
itemsRef.get().then(querySnapshot => {// fetching dataset Symptom collection all documents using snapshot
querySnapshot.docs.forEach(doc => {storedTags.push({'name':doc.id});});//on each and storing it in 'name' key : symptom name form as aked by autotags component
});
return {
storedTags: storedTags
}
break
default:
return state;
}
};
export default tagsReducer
function:
export const addTags = (tag_id) => {
return {
type: ADD_TAGS,
payload: tag_id
}
};
I had a similar error with my code since I too am using an array like you have for your currentTags. I was able to resolve my issue by adding conditional check in my reducer.
The issue is that you are trying to use spread operator for currentTags but in initialState it is empty. I think the below may be your solution and may solve your problem based on what worked for me. For your ADD_TAGS code, replace it with below:
case ADD_TAGS:
return currentTags ?
{
...state,
currentTags: [...state.currentTags, action.payload]
};
:
{
...state,
currentTags: [state.currentTags, action.payload]
};
break

connect() does not re-render component

a component dispatches an action which modifies the Redux store and the other component should get the changed state to props and rerender.
The thing is, the component gets the props, and they are correct and modified, but the component is never rerendered.
Could someone help, been stuck too much..
Component who uses store:
on mount it does a http request,
and should rerender when the state is changed.
class CalendarView extends Component {
componentDidMount() {
axios.get('http://localhost:3000/api/bookings/get')
.then(foundBookings => {
this.props.getBookings(foundBookings);
})
.catch(e => console.log(e))
}
render() {
return (
<Agenda
items={this.props.items}
selected={this.props.today}
maxDate={this.props.lastDay}
onDayPress={this.props.setDay}
renderItem={this.renderItem}
renderEmptyDate={this.renderEmptyDate}
rowHasChanged={this.rowHasChanged}
/>
);
}
renderItem = (item) => {
return (
<View style={[styles.item, { height: item.height }]}>
<Text>Name: {item.name} {item.surname}</Text>
<Text>Time: {item.time}</Text>
</View>
);
}
renderEmptyDate = () => {
return (
<View style={styles.emptyDate}><Text>This is empty date!</Text></View>
);
}
rowHasChanged = (r1, r2) => {
console.log('hit')
return true;
}
}
const mapStateToProps = (state, ownProps) => {
return {
today: state.app.today,
lastDay: state.app.lastDay,
items: state.app.items
}
}
const mapDispatchToProps = (dispatch) => {
return {
setDay: date => dispatch(appActions.setSelectionDate(date.dateString)),
getBookings: data => dispatch(appActions.getBookings(data)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CalendarView);
Action Dispatching:
dispatches an action which modifies the state
onSubmit = (name, surname, selectionDate, selectionTime) => {
axios.post('http://localhost:3000/api/bookings/create', {
bookerName: name,
bookerSurname: surname,
bookerTime: selectionTime,
date: selectionDate
}).then(savedBookings => {
this.props.createBooking(savedBookings);
this.props.navigator.pop({
animationType: 'slide-down',
});
}).catch(e => console.log(e))
}
const mapStateToProps = state => {
//...
}
const mapDispatchToProps = (dispatch) => {
return {
createBooking: data => dispatch(appActions.createBooking(data))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NewBookingScreen);
Reducer:
case types.CREATE_BOOKING: {
const { date , bookings } = action.savedBookings.data;
let dateArr = state.items;
// formatting a booking how needed
Object.keys(dateArr).forEach(key => {
if (key == date) {
dateArr[key] = [];
bookings.map(oneBooking => {
dateArr[key].push({
name: oneBooking.bookerName,
surname: oneBooking.bookerSurname,
time: oneBooking.bookerTime,
height: Math.max(50, Math.floor(Math.random() * 150))
});
})
}
});
return {
...state,
items: dateArr
};
}
full repo if needed: https://github.com/adtm/tom-airbnb/tree/feature/redux
Thank You in advance!
Your reducer is mutating the state, so connect thinks nothing has changed. In addition, your call to map() is wrong, because you're not using the result value.
Don't call push() on an array unless it's a copy. Also, please don't use any randomness in a reducer.
For more info, see Redux FAQ: React Redux ,Immutable Update Patterns, and Roll the Dice: Random Numbers in Redux .

get latest state before dispatch react-navigation & redux

I was just wondering if I call a dispatch and send props using redux and navigate to another component in the same function, How do I ensure that I get the latest props before it render the component?
................
onSubmit() {
const { navigate } = this.props.navigation;
this.props.correctAnswer(30);
navigate('Another_Component',{score: this.props.totalScore});
}
return {
render(
<View>
<TouchableOpacity onPress={() => this.onSubmit()}>
<Text>ADD 30</Text>
</TouchableOpacity>
</View>
);
}
const mapStateToProps = (state) => {
return {
totalScore: state.CurrentActScore
}
}
const mapDispatchToProps = (dispatch) => {
return {
correctAnswer: (data) => {
dispatch({ type: 'ADD_SCORE', value: data})
}
}
}
...........
right now it still send 0, the initial state.
When I add a componentWillReceiveProps(), it still navigate with old value..
I think before it finish process and setState, it's already navigate.
You need to subscribe() to your store, and then onStoreUpdate() you need to getState and assign that to your components this.state or this.props
Once you've got the latest state you can dispatch/setState with the latest data.

When to make a Fetch call for React Native Component

I'm new to React Native and confused on how to properly utilize the provided Fetch API.
The call itself (as outlined here: http://facebook.github.io/react-native/docs/network.html) is straightforward, and I can log out a successful response, but when it comes time to render the data, it's undefined.
I would expect that I could define an empty 'movies' array, and then replace it by calling 'setState' from componentDidMount(), which would trigger a re-render. Is this assumption incorrect?
The code sample below results in the following error:
'undefined is not an object (evaluating 'allRowIDs.length')
Thanks in advance for any help!
import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View } from 'react-native';
class ReactNativePlayground extends Component {
constructor() {
super();
this.state = {
movies: []
}
}
componentDidMount() {
fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
movies: responseJson.movies
})
})
.catch((error) => {
console.error(error);
});
}
render() {
return (
<View>
<Text>test</Text>
<ListView
dataSource={this.state.movies}
renderRow={(row) => <Text>{row.title}</Text>}
/>
</View>
)
}
}
AppRegistry.registerComponent('ReactNativePlayground', () => ReactNativePlayground);
That's because you need to place the data into a ListView.DataSource:
constructor (props) {
super(props);
const ds = new ListView.DataSource({
rowHasChanged: (a, b) => a !== b
})
this.state = {
movies: ds.cloneWithRows([])
}
}
// Inside the response JSON:
this.setState({
movies: this.state.movies.cloneWithRows(responseJson.movies)
});
The React Native ListView docs demonstrate this kind of setup. Using the datasource allows for optimisations to be made when rendering lists of data (notice the rowHasChanged function for instance - which prevents needless re-rendering of a row when the data hasn't altered.