search filter with ListView reactNative - react-native

hii i'm still new to react-native i'm trying to implement a search in the listview without Refetching the data (search by name )
like searching the json file i first fetched so
how can i make it works guys ? i followed many tutorials but still can't do it the listView is allways empty
here's my code i hope i found a solution for that
import React, { Component } from "react";
import { View, Text, Image, ListView } from "react-native";
import axios from "axios";
import { SearchButton } from "./utilities/SearchButton";
import SearchBar from "react-native-searchbar";
class SearchScreen extends Component {
constructor(props) {
super(props);
this.ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
doctors: [],
specefic: []
};
}
componentWillMount() {
this.fetchdata();
}
fetchdata = () => {
axios
.get("http://localhost:3000/api/Doctor")
.then(response => this.setState({ doctors: response.data }));
};
static navigationOptions = ({ navigation }) => {
return {
headerRight: <SearchButton navigation={navigation} />
};
};
render() {
return (
<View>
<View>
<SearchBar
ref={ref => (this.props.navigation.searchBar = ref)}
data={this.state.doctors}
handleResults={results => {
this.setState({ specefic: results });
}}
iOSPadding={false}
allDataOnEmptySearch={true}
fontSize={23}
hideBack={true}
heightAdjust={-5}
/>
</View>
<View>
<ListView
enableEmptySections={true}
dataSource={this.ds.cloneWithRows(this.state.doctors)}
renderRow={service => {
return (
<View style={styles.box}>
<Image
style={styles.image}
source={{ uri: service.profileImageUrl }}
/>
<View style={styles.boxContent}>
<Text style={styles.title}>{service.nom}</Text>
<Text style={styles.description}>{service.email}</Text>
</View>
</View>
);
}}
/>
</View>
</View>
);
}
}
export default SearchScreen;

Related

Error when simulate change text react native with jest

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.

How to separate API call using react natives context API?

I am new to react native. I have following component in my project for now I have written for fetching API in same component but want to separate it out. I am getting difficulty for how can i access variable which I am using in "getAlbum" method from outside of component.
I am trying to do this using new concept context API . is this possible using context API and how?
Is there standard way to separate API call from component?
import React, { Component } from 'react';
import {
FlatList, Text, View, Image, TouchableOpacity,
} from 'react-native';
import { ActivityIndicator, Provider } from 'react-native-paper';
import axios from 'axios';
import styles from '../style/ThumbnailView.component.style';
import ErrorAlert from '../common/ErrorAlert';
import * as myConstant from '../common/Constants';
export default class HomeScreen extends Component {
// For to Navigation header
static navigationOptions = () => ({
headerTitle: 'Album Information',
});
constructor(props) {
super(props);
this.state = {
isLoading: true,
apiLoadingError: false,
};
}
getAlbums() {
const { navigation } = this.props;
const albumId = navigation.getParam('albumID', 'no data');
axios
.get(
myConstant.API + `photos?albumId=${albumId}`, {timeout: myConstant.TIMEOUT}
)
.then((response) => {
this.setState({
isLoading: false,
dataSource: response.data,
});
})
.catch(err => {
this.setState({isLoading: false, apiLoadingError: true})
});
}
componentDidMount() {
this.getAlbums();
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 30 }}>
<ActivityIndicator animating={true} size='large' />
</View>
);
}
if (this.state.apiLoadingError) {
return (
<ErrorAlert />
);
}
return (
<React.Fragment>
<Provider>
<View style={styles.listContainer} >
<FlatList
testID='flatlist'
data={ this.state.dataSource } numColumns={3}
renderItem={({ item }) => <View style={styles.listRowContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('AlbumDetailsViewScreen', {
albumTitle: item.title, albumImg: item.url
})} style={styles.listRow}>
<View style={styles.listTextNavVIew}>
<Image source = {{ uri: item.thumbnailUrl }} style={styles.imageViewContainer} />
</View>
</TouchableOpacity>
</View>
}
keyExtractor = { (item, index) => index.toString() }
/>
</View>
</Provider>
</React.Fragment>
);
}
}

How to separate axios call from component in react native?

I am new to react native. I have following component in my project for now I have written for fetching API in same component but want to separate it out. I am getting difficulty for how can i access variable which I am using in "getAlbum" method from outside of component.
Is there standard way to separate API call from component?
import React, { Component } from 'react';
import {
FlatList, Text, View, Image, TouchableOpacity,
} from 'react-native';
import { ActivityIndicator, Provider } from 'react-native-paper';
import axios from 'axios';
import styles from '../style/ThumbnailView.component.style';
import ErrorAlert from '../common/ErrorAlert';
import * as myConstant from '../common/Constants';
export default class HomeScreen extends Component {
// For to Navigation header
static navigationOptions = () => ({
headerTitle: 'Album Information',
});
constructor(props) {
super(props);
this.state = {
isLoading: true,
apiLoadingError: false,
};
}
getAlbums() {
const { navigation } = this.props;
const albumId = navigation.getParam('albumID', 'no data');
axios
.get(
myConstant.API + `photos?albumId=${albumId}`, {timeout: myConstant.TIMEOUT}
)
.then((response) => {
this.setState({
isLoading: false,
dataSource: response.data,
});
})
.catch(err => {
this.setState({isLoading: false, apiLoadingError: true})
});
}
componentDidMount() {
this.getAlbums();
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 30 }}>
<ActivityIndicator animating={true} size='large' />
</View>
);
}
if (this.state.apiLoadingError) {
return (
<ErrorAlert />
);
}
return (
<React.Fragment>
<Provider>
<View style={styles.listContainer} >
<FlatList
testID='flatlist'
data={ this.state.dataSource } numColumns={3}
renderItem={({ item }) => <View style={styles.listRowContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('AlbumDetailsViewScreen', {
albumTitle: item.title, albumImg: item.url
})} style={styles.listRow}>
<View style={styles.listTextNavVIew}>
<Image source = {{ uri: item.thumbnailUrl }} style={styles.imageViewContainer} />
</View>
</TouchableOpacity>
</View>
}
keyExtractor = { (item, index) => index.toString() }
/>
</View>
</Provider>
</React.Fragment>
);
}
}
You can separate your axios call by making another class with function which will receive 'albumID' as an argument - then add it to your axios link. If you want to call this function from another class just make it static and use like in example below. Then you can map your fetchData to parse it into state. Hope it will help you.
export class Api {
static fetchData = (albumId: string) => {
//here your axios call which will return an array
}
}
export default class HomeScreen extends React.Component {
state = {
//.....
}
receivedData = Api.fetchData('albumID');
//you can map array here to get what you want.
}

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.

Shoutem fetch data not displaying

First I want to start by saying I am a total noob at React Native and Shoutem. I am not sure if I should write Shoutem or React Native in the subject of my questions, but here is my problem.
I have two screens:
Screen1.js
Screen2.js
Screen1 displays a list of items returned from a fetch. Once I click on the item, it will open the second screen which is the details screen.
I am passing data from screen1 to screen2. In screen2 I need to make another fetch call for different data, but it does not work. I am doing exactly the same thing on both screens.
Here is my code for Screen1:
import React, {
Component
} from 'react';
import {
ActivityIndicator,
TouchableOpacity
} from 'react-native';
import {
View,
ListView,
Text,
Image,
Tile,
Title,
Subtitle,
Overlay,
Screen
} from '#shoutem/ui';
import {
NavigationBar
} from '#shoutem/ui/navigation';
import {
navigateTo
} from '#shoutem/core/navigation';
import {
ext
} from '../extension';
import {
connect
} from 'react-redux';
export class List extends Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.state = {
isLoading: true,
content: null,
}
}
componentDidMount() {
return fetch('https://www.cannabisreports.com/api/v1.0/strains').then((response) => response.json()).then((responseData) => {
this.setState({
isLoading: false,
content: responseData
});
}).done();
}
renderRow(rowData) {
const { navigateTo } = this.props;
return (
//<Text>{rowData.name}, {rowData.createdAt.datetime}</Text>
<TouchableOpacity onPress={() => navigateTo({
screen: ext('Strain'),
props: { rowData }
})}>
<Image styleName="large-banner" source={{ uri: rowData.image &&
rowData.image ? rowData.image : undefined }}>
<Tile>
<Title>{rowData.name}</Title>
<Subtitle>none</Subtitle>
</Tile>
</Image>
</TouchableOpacity>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{flex: 1, paddingTop: 20}}>
<ListView
data={this.state.content.data}
renderRow={rowData => this.renderRow(rowData)}
/>
</View>
);
}
}
// connect screen to redux store
export default connect(
undefined,
{ navigateTo }
)(List);
I am passing rowData to Screen2. I then need to make another fetch calling using data from rowData as it is a path parameter needed for the API call in Screen2.
So basically I need to make a fetch call in Screen2 like this:
fetch('https://mywebsite.com/'+rowData.something+'/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
content: responseJson.data
})
})
.catch((error) => {
console.error(error);
});
Here is my code for screen2:
export default class Strain extends Component {
constructor(props) {
super(props);
this.state = {
content: null,
}
}
componentDidMount() {
return fetch('https://mywebsite.com/'+rowData.something+'/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
content: responseJson.data
})
})
.catch((error) => {
console.error(error);
});
}
renderRow(dataContent) {
return (
<Text>{dataContent.name}</Text>
// This does not work either -> <Text>{dataContent}</Text>
);
}
render() {
const { rowData } = this.props; //This is coming from screen1.js
return (
<ScrollView style = {{marginTop:-70}}>
<Image styleName="large-portrait" source={{ uri: rowData.image &&
rowData.image ? rowData.image : undefined }}>
<Tile>
<Title>{rowData.name}</Title>
<Subtitle>{rowData.createdAt.datetime}</Subtitle>
</Tile>
</Image>
<Row>
<Text>Seed Company: {rowData.seedCompany.name}</Text>
</Row>
<Divider styleName="line" />
<Row>
<Icon name="laptop" />
<View styleName="vertical">
<Subtitle>Visit webpage</Subtitle>
<Text>{rowData.url}</Text>
</View>
<Icon name="right-arrow" />
</Row>
<Divider styleName="line" />
<View style={{flex: 1, paddingTop: 20}}>
<ListView
data={content}
renderRow={dataContent => this.renderRow(dataContent)}
/>
</View>
<Divider styleName="line" />
</ScrollView>
);
}
}
My fetch URL returns data like this:
{
data: {
name: "7.5833",
another_name: "8.6000",
different_name: "5.7500",
}
}
This only returns one data object like what you see above.
When I run the code I get this error:
null is not an object (evaluating 'Object.keys(e[t])')
Please let me know if you need me to provide more info.
I have tried so many different things and nothing seems to work so I am in need of some help. What am I doing wrong?
Not sure why this works but it does.
I used a function to fetch my data and then call the function in componentDidMount like this:
getData() {
return fetch('https://mywebsite.com/myotherdata')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
data: responseJson.data
});
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getData();
}
Then to get the values from the JSON response I am doing this:
this.state.data.name
I am having another issue, but I will create another question.