Error when simulate change text react native with jest - react-native

Let's say I create a login screen. Inside that screen, I import form component. And inside form component I have a text input.
Then, I want to simulate text input on change text, but always get an error
Method “simulate” is meant to be run on 1 node. 0 found instead.
This is my test file
it('calls the login submit method', () => {
const fieldPhoneNumber = wrapper
.find('Form')
.dive()
.find('TextInput[id="fieldPhoneNumber"]');
fieldPhoneNumber
.at(0)
.simulate('changeText', { target: { value: '082262366193' } });
});
This is my component login file
import React, { useState, useEffect } from 'react';
import { ScrollView, StatusBar, Platform } from 'react-native';
import Header from './components/Header';
import Form from './components/Form';
import ButtonSocialMedia from './components/ButtonSocialMedia';
function LoginScreen() {
const [phoneNumber, setPhoneNumber] = useState('');
const [focus, setFocus] = useState(false);
useEffect(() => {
}, [phoneNumber]);
const changePhoneNumber = (value) => {
setPhoneNumber(value);
};
const showAppleButton = () => {
if (Platform.OS === 'ios') {
const version = Platform.Version.split('.')[0];
if (version >= 13) {
return true;
} else {
return false;
}
} else {
return false;
}
};
const loginSubmit = () => {
console.log('Login Submit');
};
return (
<ScrollView>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="light-content"
/>
<Header />
<Form
phoneNumber={phoneNumber}
changePhoneNumber={(value) => changePhoneNumber(value)}
focus={focus}
setFocus={() => setFocus(true)}
loginSubmit={() => loginSubmit()} />
<ButtonSocialMedia showAppleButton={() => showAppleButton()} />
</ScrollView>
);
}
export default LoginScreen;
This is my form component
/* eslint-disable prettier/prettier */
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import styles from '../styles/StyleForm';
import color from '../../../../__global__/styles/themes/colorThemes';
import regex from '../../../../constant/regex';
import * as yup from 'yup';
import { Formik } from 'formik';
import ButtonFull from '../../../../__global__/button/buttonFull';
const regexPhoneNumber = regex.phone;
function Form(props) {
const renderFocus = () => {
if (props.focus) {
return (
<Text style={styles.textFocus}>Type your phone number</Text>
);
}
};
return (
<Formik
enableReinitialize={true}
initialValues={{
phoneNumber: props.phoneNumber,
}}
onSubmit={values => {
console.log('Login Submit');
}}
validateOnMount={true}
validationSchema={yup.object().shape({
phoneNumber: yup
.string()
.required()
.min(8)
.matches(regexPhoneNumber, 'Phone number is not valid'),
})}>
{({
// values,
handleChange,
errors,
setFieldTouched,
touched,
isValid,
handleSubmit,
}) => (
<View style={styles.form}>
<View style={styles.subContainer}>
<View style={styles.containerTitle}>
<Text style={styles.textTitle}>+62</Text>
</View>
<View style={styles.containerPhoneNumber}>
{renderFocus()}
<TextInput
id={'fieldPhoneNumber'}
onFocus={() => props.setFocus(true)}
value={props.phoneNumber}
style={styles.subContainerPhoneNumber}
placeholderStyle={styles.placeholder}
placeholder={'Type your phone number'}
onChangeText={(value) => {
handleChange('phoneNumber');
props.changePhoneNumber(value);
setFieldTouched('phoneNumber', true);
}}
keyboardType={'numeric'}
onBlur={() => setFieldTouched('phoneNumber', true)}
/>
</View>
</View>
{touched.phoneNumber && errors.phoneNumber && (
<View style={styles.containerError}>
<Text style={styles.textError}>Phone number is not valid</Text>
</View>
)}
<View style={styles.containerButton}>
<ButtonFull
isDisabled={!isValid}
id={'buttonLogin'}
color={isValid ? color.thema : color.grey}
handleSubmit={() => props.loginSubmit()}
title={'Next'}
/>
</View>
</View>
)}
</Formik>
);
}
export default Form;

The error you're facing implies that the statement const fieldPhoneNumber wrapper.find('Form').dive().find('TextInput[id="fieldPhoneNumber"]'); couldn't find the TextInput component and hence the simulate function cannot be called. Try searching for the string "TextInput" inside the wrapper, and see if that works.

Related

component is not defined while opened on web

I have a component that work fine on android but it shows an error when I try to open it on the web using expo
Uncaught ReferenceError: TextInputMask is not defined
at ./src/components/atoms/TextInputMask.js (TextInputMask.js:6:1)
Here is TextInputMask code that gives an error
import React, { useEffect, useState } from "react";
import { View, StyleSheet } from "react-native";
import { TextInput, Text } from "react-native-paper";
import { TextInputMask as TextInputMaskDefault } from "react-native-masked-text";
export default TextInputMask = React.forwardRef(
(
{
label,
placeholder,
style,
mode = "outlined",
onChangeValue = () => {},
type = "custom",
options = {},
value,
icon,
disabled,
error,
...props
},
ref
) => {
const [data, setData] = useState();
const [maskValue, setMaskValue] = useState();
useEffect(() => {
if (value === null || value === undefined) {
setMaskValue("");
return;
}
if (value) {
value = String(value);
setMaskValue(value);
}
}, [value]);
useEffect(() => {
let rawValue = data?.getRawValue();
onChangeValue(rawValue, maskValue);
}, [maskValue]);
return (
<View style={style}>
<View style={styles.viewInput}>
<TextInput
style={styles.textInput}
label={label}
placeholder={placeholder}
mode={mode}
autoCapitalize="none"
autoCorrect={false}
autoCompleteType="off"
ref={ref}
disabled={disabled}
value={maskValue}
error={error}
render={(inputProps) => {
return (
<TextInputMaskDefault
{...inputProps}
type={type}
options={options}
onChangeText={(text) => setMaskValue(text)}
ref={(ref) => setData(ref)}
/>
);
}}
{...props}
/>
{icon ? (
<Text style={styles.textInputIcon}>
<TextInput.Icon
name={icon}
onPress={onPress}
size={30}
color={error ? Colors.red800 : null}
disabled={disabled}
/>
</Text>
) : null}
</View>
</View>
);
}
);
Any idea where the error might be? I wonder if there is a difference in creating component for android and web?

Unable to store data in redux store

i am trying to build a react-native app in that user can add a routine or daily task in EditRoutine.js file and it can be seen in RoutineOverviewScreen.js and i am using redux for storing these data and using hooks for storing and fetching data.
Below is the EditRoutine.js code snippet
import React, { useState, useEffect, useCallback } from "react";
import { View, StyleSheet, Text, TextInput, ScrollView } from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import Card from "../components/Card";
import { useSelector, useDispatch } from "react-redux";
import * as routinesActions from "../store/actions/routine";
import Routine from "../models/routine";
import HeaderButton from "../components/HeaderButton";
const EditRoutine = (props) => {
const dispatch = useDispatch();
const [title, setTitle] = useState("");
const [detail, setDetail] = useState("");
const [time, setTime] = useState("");
const submitHandler = useCallback(() => {
dispatch(routinesActions.createRoutine(title, detail, time));
props.navigation.goBack();
}, [dispatch,title, detail, time]);
useEffect(() => {
props.navigation.setParams({ submit: submitHandler });
}, [submitHandler]);
return (
<Card style={styles.container}>
<Text>Title</Text>
<TextInput
style={styles.input}
value={title}
onChangeText={(text) => setTitle(text)}
/>
<Text>Details</Text>
<TextInput
style={styles.input}
multiline
numberOfLines={4}
value={detail}
onChangeText={(text) => setDetail(text)}
/>
<Text>Time</Text>
<TextInput
style={styles.input}
value={time}
onChangeText={(text) => setTime(text)}
/>
</Card>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
padding: 10,
width: "100%",
},
input: {
paddingHorizontal: 2,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
width: "100%",
marginVertical: 15,
},
});
EditRoutine.navigationOptions = (navData) => {
const submitFn = navData.navigation.getParam("submit");
return {
headerTitle: "Edit Routine",
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={
Platform.OS === "android" ? "md-arrow-back" : "ios-arrow-back"
}
onPress={() => {
navData.navigation.goBack();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Save"
iconName={
Platform.OS === "android" ? "md-checkmark" : "ios-checkmark"
}
onPress={submitFn}
/>
</HeaderButtons>
),
};
};
export default EditRoutine;
and this is my RoutineOverviewScreen.js file where i am trying to show the created routine
import React from "react";
import { View, StyleSheet, Text, FlatList } from "react-native";
import Card from "../components/Card";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import { useSelector } from "react-redux";
const RoutineOverViewScreen = (props) => {
const routines = useSelector((state) => state.routines.myRoutine);
return (
<FlatList
data={routines}
keyExtractor={(item) => item.id}
renderItem={(itemData) => (
<Card>
<View>
<Text>{itemData.item.id} </Text>
</View>
<View>
<Text>{itemData.item.title} </Text>
</View>
<View>
<Text>{itemData.item.detail} </Text>
<View>
<Text>{itemData.item.time} </Text>
</View>
</View>
</Card>
)}
/>
);
};
const styles = StyleSheet.create({});
RoutineOverViewScreen.navigationOptions = (navData) => {
return {
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Add"
iconName={
Platform.OS === "android" ? "md-add-circle" : "ios-add-circle"
}
onPress={() => {
navData.navigation.navigate("Edit");
}}
/>
</HeaderButtons>
),
};
};
export default RoutineOverViewScreen;
Below is my action file routine.js snippet
export const CREATE_ROUTINE= 'CREATE_ROUTINE';
export const deleteRoutine = routineId => {
return { type: DELETE_ROUTINE, pid: routineId };
};
export const createRoutine = (title, detail, time) => {
return {
type: CREATE_ROUTINE,
routineData: {
title,
detail,
time
}
};
};
Below is my reducer file reducer.js snippet
import {
DELETE_ROUTINE,
CREATE_ROUTINE,
UPDATE_ROUTINE,
} from "../actions/routine";
import Routine from "../../models/routine";
const initialState = {
myRoutine: {},
id: 1,
};
export default (state = initialState, action) => {
switch (action.type) {
case CREATE_ROUTINE:
const newRoutine = new Routine(
state.id,
action.routineData.title,
action.routineData.detail,
action.routineData.time
);
return {
...state,
items: { ...state.items, [state.id]: newRoutine },
id: state.id + 1,
};
default: {
return state;
}
}
return state;
};
and this is my app.js file snippet
import React, { useState } from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import routinesReducer from './store/reducers/routine';
import AppNavigator from './navigator/RoutineNavigator';
const rootReducer = combineReducers({
routines: routinesReducer,
});
const store = createStore(rootReducer);
const fetchFonts = () => {
return Font.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf')
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => {
setFontLoaded(true);
}}
/>
);
}
return (
<Provider store={store}>
<AppNavigator />
</Provider>
);
}
with these above code i am able to create a new routine but i am not knowing whether my input are getting stored in to the App central state because when i am trying to render those saved data i am unable to see in my RoutineOverviewScreen screen.please help me and
About Me: Govind Kumar Thakur ( iamgovindthakur )
email: iamgovindthakur#gmail.com
Thank You :)
You are trying to use the data of "myRoutine" but never save data to this property in the reducer.
I think that the issue you are having is due to the following line in your reducer:
items: { ...state.items, [state.id]: newRoutine },

Saving list with AsyncStorage

So I made a "notepad" app and I want to do so the text that the user wrote etc it should be saved, so the text doesn't get reset when user quits the app.
I'm new to react-native, after a few google searches I need AsyncStorage? to make this happen.
but really dunno on how to do it.
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
TouchableWithoutFeedback,
TouchableOpacity,
Keyboard,
AsyncStorage
} from 'react-native';
import Header from './components/header';
import ListItem from './components/listitem';
import AddList from './components/addlist';
export default function App() {
const [todos, setTodos] = useState([
]);
const pressHandler = (key) => {
setTodos((prevTodos) => {
return prevTodos.filter(todo => todo.key != key);
});
}
const submitHandler = (text) => {
if(text.length > 0) {
setTodos((prevTodos) => {
return [
{ text: text, key: Math.random().toString() },
...prevTodos
];
})
}
}
return (
<TouchableWithoutFeedback onPress={() => {
Keyboard.dismiss();
}}>
<View style={styles.container}>
<Header />
<View style={styles.content}>
<AddList submitHandler={submitHandler} />
<View style={styles.todoList}>
<FlatList
data={todos}
renderItem={({ item }) => (
<ListItem item={item} pressHandler={pressHandler} />
)}
/>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}
new problem out of nowhere worked great before now broken without touching the code
const pressHandler = key =>
setTodos(prevTodos => {
const newTodos = prevTodos.filter(todo => todo.key !== key);
storeTodosInAsync(newTodos);
console.log(prevTodos);
return newTodos;
});
const submitHandler = text => {
if (text.length > 0) {
const key = Math.random().toString();
setTodos(prevTodos => {
const newTodos = [{ text, key }, ...prevTodos];
storeTodosInAsync(newTodos);
console.log(newTodos);
return newTodos;
});
}
};
You can use AsyncStorage to store and load data to/from local storage. One thing to note is data MUST be a string, so anything like an object that is not a string needs to be stringified. You can use JSON.stringify(...) to do this. And then when you get the string back you can use JSON.parse(...) to convert it back into an object.
So to convert your current code into something that automatically loads saved todos and always saves the latest, you could write this:
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
TouchableWithoutFeedback,
TouchableOpacity,
Keyboard,
AsyncStorage,
Button
} from 'react-native';
import Header from './components/header';
import ListItem from './components/listitem';
import AddList from './components/addlist';
export default function App() {
const [todos, setTodos] = useState([]);
useEffect(() => {
restoreTodosFromAsync();
}, []);
const pressHandler = key => {
console.log('Todos BEFORE delete');
console.log(todos);
const newTodos = todos.filter(todo => todo.key !== key);
console.log('Todos AFTER delete');
console.log(todos);
setTodos(newTodos);
storeTodosInAsync(newTodos);
};
const submitHandler = text => {
if (text.length === 0) return;
const key = Math.random().toString();
console.log('Todos BEFORE submit');
console.log(todos);
const newTodos = [{ text, key }, ...todos];
console.log('Todos AFTER submit');
console.log(todos);
setTodos(newTodos);
storeTodosInAsync(newTodos);
};
const asyncStorageKey = '#todos';
const storeTodosInAsync = newTodos => {
const stringifiedTodos = JSON.stringify(newTodos);
AsyncStorage.setItem(asyncStorageKey, stringifiedTodos).catch(err => {
console.warn('Error storing todos in Async');
console.warn(err);
});
};
const restoreTodosFromAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then(stringifiedTodos => {
console.log('Restored Todos:');
console.log(stringifiedTodos);
const parsedTodos = JSON.parse(stringifiedTodos);
if (!parsedTodos || typeof parsedTodos !== 'object') return;
setTodos(parsedTodos);
})
.catch(err => {
console.warn('Error restoring todos from async');
console.warn(err);
});
};
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.container}>
<Header />
<View style={styles.content}>
<AddList submitHandler={submitHandler} />
<View style={styles.todoList}>
<FlatList
data={todos}
renderItem={({ item }) => <ListItem item={item} pressHandler={pressHandler} />}
/>
</View>
</View>
</View>
</TouchableWithoutFeedback>
);
}

How to deal with delay api response in react native using redux?

Im building a react-native app, which when the user try to signIn, I invoike firebase.CreateUser and then an api from firebase function to create that user in my database (Firebase Real-Time). The problem is that when the componentDidUpdate is executed, I still don't have the result from my firebaseFunction, then my props only update if I tap in screen. I would like to know how to deal with that.
Im using redux.
Follow my code:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Button, Image,Alert} from 'react-native';
import logo from '../../asserts/logo.png'
import { TouchableOpacity, TextInput } from 'react-native-gesture-handler';
import { Divider,Input} from 'react-native-elements';
import axios from 'axios';
import { connect } from 'react-redux';
import { signupUser} from '../../store/actions/Section/actions';
class Signin extends Component {
state = {
fullName:'',
userName:'',
email:'',
password:'',
confirmPassword:'',
bornDate:'',
State:'',
City:''
};
handleEmailChange = val => {
this.setState({ email:val });
};
handlePasswordChange = val => {
this.setState({ password:val });
};
handleConfirmPasswordChange = val => {
this.setState({ confirmPassword:val });
};
handleNameChange = val => {
this.setState({ fullName:val });
};
handleUserNameChange = val => {
this.setState({ userName:val });
};
handleStateChange = val => {
this.setState({ State:val });
};
handleCityChange = val => {
this.setState({ City:val });
};
handleBornDateChange = val => {
this.setState({ bornDate:val });
};
onSignInUser = () => {
const {email,password} = this.state
if(email=='' || password=='')
return;
this.props.signUp(this.state.fullName,this.state.userName, this.state.email,this.state.password,this.state.confirmPassword,this.state.bornDate,this.state.State,this.state.City);
// this.props.navigation.navigate('User');
};
componentDidUpdate() {
const { idUser, loading,error } = this.props;
console.log(idUser);
console.log('aqui');
if (!loading && error) Alert.alert('Erro', error);
if (!loading && idUser) this.props.navigation.navigate('User');
}
render() {
return (
<View style={styles.container}>
<View style={styles.flexCenter}>
<Image source={logo} style={styles.logoImage}/>
<Text style={styles.logoText} >HomeShare</Text>
<Text style={styles.sublogoText} >SignUp</Text>
</View>
<Divider style={styles.divider} />
<View style={styles.flexButton}>
<View style={styles.inputContainer}>
<Input style={styles.textInput} onChangeText={this.handleNameChange} value={this.state.fullName} placeholder='Nome'/>
<Input style={styles.textInput} onChangeText={this.handleUserNameChange} value={this.state.userName} placeholder='User'/>
<Input style={styles.textInput} onChangeText={this.handleBornDateChange} value={this.state.bornDate} placeholder='Nascimento'/>
<Input style={styles.textInput} onChangeText={this.handleStateChange} value={this.state.State} placeholder='Estado'/>
<Input style={styles.textInput } onChangeText={this.handleCityChange} value={this.state.City} placeholder='Cidade'/>
<Input style={styles.textInput} onChangeText={this.handleEmailChange} value={this.state.email} placeholder='E-mail' keyboardType={'email-address'}/>
<Input style={styles.textInput} onChangeText={this.handlePasswordChange} value={this.state.password} placeholder='Senha' secureTextEntry={true}/>
<Input style={styles.textInput} onChangeText={this.handleConfirmPasswordChange} value={this.state.confirmPassword} placeholder='Confirme sua Senha' secureTextEntry={true}/>
</View>
<TouchableOpacity style={styles.button} activeOpacity={0.5} onPress={this.onSignInUser} >
<View>
<Text style={styles.buttonText}>SignIn</Text>
</View>
</TouchableOpacity>
<Text style={{marginTop:10}}>Ou</Text>
<TouchableOpacity style={styles.button} activeOpacity={0.5} onPress={this.signInUser}>
<View>
<Text style={styles.buttonText}>Entrar com Facebook</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
}
const mapStateToProps = ({ section: { restoring, loading, user, error, logged, idUser } }) => ({
restoring: restoring,
loading: loading,
user: user,
error: error,
logged: logged,
idUser: idUser
});
const mapDispatchToProps = {
signUp:signupUser
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Signin);
My Action:
export const signupUser = (fullName,userName, email,password,confirmPassword,bornDate,State,City) => dispatch => { dispatch(sessionLoading());
firebaseService.auth().createUserWithEmailAndPassword(email, password).then(user => {
console.log(user);
firebaseService.auth().currentUser.getIdToken(true).then(function(idToken) {
SectionService.signIn(idToken,fullName,userName, email,password,confirmPassword,bornDate,State,City).then((response) =>{
console.log(response);
dispatch(sessionSetId(response));
}).catch(e=> {
dispatch(sessionError(e));
});
}).catch(function(error) {
dispatch(sessionError(e));
});
})
.catch(error => {
dispatch(sessionError(error.message));
});
A proposed solution is to handle the account creation in the createUser callback and to update it with other data in the cloud function. Alternatively you can set up a listener that looks for the document, which will then be created and the listener will be notified.
I personally create the user doc on the client side because I create it with some data only available on the client, but your use case will be dictate your preferred approach.

How to call the redux actions of one component in another

I'm rendering the data got from an API into the Cards, I created a CardContainers component that map the data I get from the API then use that component in another component.
CardContainers.js
import React from 'react';
import {View} from 'react-native';
import {withNavigation} from 'react-navigation';
class CardContainers extends React.Component{
addPlace(){
return this.props.addPlace;
}
renderCards(){
return this.props.data.map((item, index) => {
return (
<View key={index}>
{this.props.renderCard(item)}
</View>
)
})
}
render(){
return(
<View>{this.renderCards()}</View>
)
}
}
PlacesList.js
import React from 'react';
import ProgressiveInput from 'react-native-progressive-input';
import {StyleSheet, View, Alert, Text, TouchableOpacity, ListView, ScrollView} from 'react-native';
import {Button, Card, Icon} from 'react-native-elements';
import {connect} from 'react-redux';
import CardContainers from './CardContainers';
import * as placesActions from '../redux/actions/placesActions';
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
class PlacesList extends React.Component{
static navigationOptions = ({navigation}) => {
return {
title:'Finding new places',
}
}
constructor(props){
super(props)
const position = this.props.navigation.getParam('position')
const tripId = JSON.stringify(this.props.navigation.getParam('tripId')).replace(/\"/g,"");
const date = JSON.stringify(this.props.navigation.getParam('date')).replace(/\"/g,"");
this.state={
position: position,
tripId: tripId,
date: date,
dataSource:ds.cloneWithRows([]),
}
}
renderCard(item){
return(
<Card
key={item.id}
title={item.title}
titleStyle={styles.title}
containerStyle={{marginTop: 20, marginBottom:20}}
>
<View style={{flexDirection: 'row'}}>
<Text style={{margin:10}}>
Category: {item.category.title}
</Text>
<Text style={{margin:10}}>
Rating: {item.averageRating}
</Text>
</View>
<View style ={{flexDirection:'row', alignItems:'center', alignSelf:'center'}}>
<Button
icon={<Icon name='add' color='#ffffff'/>}
buttonStyle={{marginLeft:15, borderRadius:10}}
title='ADD THIS PLACE'
type='solid'
onPress={()=>this.props.addPlace()}
/>
</View>
</Card>
);
}
getPlacesAroundDestination = () => {
this.props.aroundPlaces(this.state.position);
this.setState({dataSource: ds.cloneWithRows(this.props.placesAround)})
}
autoComplete = async(query) => {
this.setState({destination: query})
await this.props.suggestPlaces(this.state.position, query)
this.setState({dataSource: ds.cloneWithRows(this.props.searchSuggests)})
}
inputCleared = () => {
this.setState({
destination:'',
isLoading: false,
dataSource: ds.cloneWithRows({}),
});
}
onListItemClicked = (searchSuggests) => {
this.setState({
title: searchSuggests.title,
placeId: searchSuggests.id,
openingHours: searchSuggests.openingHours,
category: searchSuggests.category,
position:searchSuggests.position.toString(),
dataSource:ds.cloneWithRows([]),
})
}
renderRow = (searchSuggests) => {
return(
<TouchableOpacity
style={{padding:10}}
onPress={()=>this.onListItemClicked(searchSuggests)}
>
<Text style={{fontSize:20}}>{searchSuggests.title}</Text>
<Text style={{fontSize:10}}>{searchSuggests.vicinity}</Text>
</TouchableOpacity>
)
}
renderSeparator = () => {
return <View style={{borderWidth:0.5, borderColor:'lightgrey',}}> </View>
}
renderContent(){
return (
<CardContainers
data={this.props.placesAround.items}
renderCard={this.renderCard}
/>
)
}
render(){
return(
<View style={styles.container}>
<ProgressiveInput
style={{marginTop:20, marginLeft:10, marginRight:10}}
placeholder='Your destination...'
value={this.state.destination}
isLoading={this.props.isLoading}
onChangeText={this.autoComplete}
onInputCleared={this.inputCleared}
/>
<View style={{flex:0}}>
<ListView
enableEmptySections
style={{backgroundColor:'white', margin:20}}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
renderSeparator={this.renderSeparator}
/>
</View>
<Button
title= 'SUGGEST'
style={{alignSelf:'center'}}
onPress={() => this.props.aroundPlaces(this.state.position)}
/>
<ScrollView>
{this.props.placesAround.items? this.renderContent():null}
</ScrollView>
</View>
)
}
}
const mapStateToProps = (state) => {
return{
searchSuggests: state.places.searchSuggests,
isLoading: state.places.isLoading,
placesAround: state.places.placesAround,
geolo: state.location.latitude + ',' + state.location.longitude,
}
}
const mapDispatchToProps = (dispatch) => {
return {
addPlace:(tripId, date, title, category, rating, placeID) => dispatch (placesOfPlanActions.addPlace(tripId, date, title, category, rating, placeID)),
aroundPlaces: (geolo) => dispatch(placesActions.aroundPlaces(geolo)),
suggestPlaces: (geolo, destination) => dispatch(placesActions.suggestPlaces(geolo, destination))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PlacesList);
As you can see in the code below, I want to call the addPlace() function which is a redux action in the onPress event of each Card rendered, but I cannot do it because the CardContainers does not have that function inside. So is there any way that I can do it? I'm quite new to react-native and redux, just spent 4 months on this and I do not think that I fully understand it.
Yup, simply add the connect HOC to CardContainers and you should be able to set the function in mapDispatchToProps like you did in PlacesList.