Implement onPress on Flatlist item - react-native

I am trying to send the data of the flatlist items when clicked and set to another class.The ontouch is working but I am having the error below in the image. Also how can I send the data of api to the other class and get from another class? I have implemented as follows:
export default class FlatSpeakers extends Component {
constructor(props) {
super(props);
this.state = { isLoading: true, data: [],selectedItem: null, }
const { navigate } = this.props.navigation;
}
onPressItem = () => {
navigate('SpeakersClick')
};
componentDidMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(res => {
this.setState({
isLoading: false,
data: res.data,
})
})
}
renderItem({ item }) {
return (
<TouchableOpacity onPress={()=>this.onPressItem(item)} >
<Card>
<CardSection>
<View style={styles.thumbnailContainerStyle}>
<Image
style={styles.thumbnailStyle}
source={{ uri: item.image }}
/>
</View>
<View style={styles.headerContentStyle}>
<Text style={styles.headerTextStyle}>{item.title}</Text>
<Text>{item.artist}</Text>
</View>
</CardSection>
</Card>
</TouchableOpacity>
)
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={(item, index) => index}
onPress={this.onPressItem}
/>
</View>
);
}
}

Problem in your code is, that you are calling same method from two sides - on one side you are passing arguments in it on another side you are not passing arguments. If you wan't to have both cases covered you should change you onPressFunction, to accept arguments - in your case item:
onPressItem = (item) => {
navigate('SpeakersClick')
};

try to put this.onPressItem = this.onPressItem.bind(this) on constructor(props)

Related

Reset all the state of child and parent class by a button in the parent class - React native

I've a parent class with its child classes. When the child class is clicked, its id is passed to parent class and the bg color of the child is changed.
I want to reset all the state and bgColor of child class by clicking the button in the parent class. How can I achieve it?
Thanks in advance.
Parent class:
getSelectedChilds = (id) => {
const items = this.state.selectedIds.filter(item => item.id !== id);
this.setState({
selectedIds: [...items]
});
}
render() {
return(
<View>
<FlatList
data={data}
renderItem={({ item, index }) => <Child getSelectedChilds={this.getSelectedChilds} item={item} />}
/>
<Button
onPress={() => {
// how to reset all the states (parent & child) by clicking this button?
}}
title="Submit"
/>
</View>
)
}
Child class
export default class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
selectChild: false
}
}
selectedChild = (id) => {
this.setState({
selectChild: !this.state.selectChild
});
this.props.getSelectedChilds(id);
}
render() {
const { item } = this.props;
return (
<View style={{ flex: 1 }}>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
this.selectedChild(item.id);
}}>
<View style={[{ backgroundColor: this.state.selectChild ? 'red' : 'transparent' }]}>
<View style={{ flexDirection: 'row' }}>
<Text>{item.name}</Text>
</View>
</View>
</TouchableOpacity>
</View>
)
}
}
What I did so far:
I used refs to access the function of child. It works but the state of child classes can not be changed, and hence bgColor is red all the time.
Parent class
constructor(props) {
super(props);
this.child = React.createRef();
}
resetAll = () => {
this.setState({
selectedIds: [...items]
});
this.child.current.reset(); // this is not working properly
}
render() {
return(
<View>
<FlatList
data={data}
renderItem={({ item, index }) => <Child ref={this.child} getSelectedChilds={this.getSelectedChilds} item={item} />}
/>
<Button
onPress={() => {
this.resetAll()
}}
title="Submit"
/>
</View>
)
}
Child class
reset = () => {
alert('abc'); //the alert works but the line after this doesn't work and the bg color is unchanged.
this.setState({
selectChild: false
});
}
render() {
const { item } = this.props;
return (
<View style={{ flex: 1 }}>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
this.selectedChild(item.id);
}}>
<View style={[{ backgroundColor: this.state.selectChild ? 'red' : 'transparent' }]}>
<View style={{ flexDirection: 'row' }}>
<Text>{item.name}</Text>
</View>
</View>
</TouchableOpacity>
</View>
)
}
You could just pass a boolean in your child to tell him to reset its own state. Something like :
const resetAll = () => {
this.setState({
// ... whatever you want,
resetChild: true
});
}
And then
<Child shouldReset={this.state.resetChild} />
By cliking on your button, you will trigger a re-render in your child and this child will handle its own state.

How to fetch data from api and set to image slider?

I am new to reactnative and I am trying to make the imagesilder. I am trying to get data from the api and set it to the imageslider. I have to get the image tag from the api and set it to the imageslider.My api is as follows:
[
{
"title":"Taylor Swift",
"artist":"Taylor Swift",
"url":"https://www.amazon.com/Taylor-Swift/dp/B0014I4KH6",
"image":"https://images-na.ssl-images-amazon.com/images/I/61McsadO1OL.jpg",
"thumbnail_image":"https://i.imgur.com/K3KJ3w4h.jpg"
},
{
"title":"Fearless",
"artist":"Taylor Swift",
"url":"https://www.amazon.com/Fearless-Enhanced-Taylor-Swift/dp/B001EYGOEM",
"image":"https://images-na.ssl-images-amazon.com/images/I/51qmhXWZBxL.jpg",
"thumbnail_image":"https://i.imgur.com/K3KJ3w4h.jpg"
}
]
I cant get the image data from the api and to render it inside the render function. I have implemented as follows:
export default class ViewpagerP extends Component {
constructor(props) {
super(props);
this.state = { isLoading: true, images: [], data: [] }
}
componentDidMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response =>response.json())
.then((data)=>{for (let index = 0; index < data.length; index++) {
this.setState({
isLoading: false,
data: data[index]
})
}})
}
render() {
const { images } = this.state;
return (
<View style={styles.container}>
<ImageSlider style={styles.viewPagerStyle}
loopBothSides
autoPlayWithInterval={1000}
images={images}
customSlide={({ index, item, style, width }) => (
<View key={index} style={style}>
<Image source={{ uri: item }} style={styles.customImage} />
</View>
)}
/>
</View>
)
}
}
As you already have images in state, utilize that only.
.then((data)=>{
this.setState({
isLoading: false,
images:data
})
})
Now you have your response within this.state.images, so just need to do one change which is inside customSlide
customSlide={({ index, item, style, width }) => (
<View key={index} style={style}>
<Image source={{ uri: item.image }} style={styles.customImage} />
</View>
)}
item will give you whole single node we need to extract image from that element, so we will write item.image
Use the code below and it will works, since you forgot to add data state in you image slider library, that's why you are not getting your desired result
export default class ViewpagerP extends Component {
constructor(props) {
super(props);
this.state = { isLoading: true, images: [], data: [] }
}
componentDidMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
data: responseJson
})
})
}
render() {
return (
<View style={styles.container}>
<ImageSlider style={styles.viewPagerStyle}
loopBothSides
autoPlayWithInterval={1000}
images={this.state.data}
customSlide={({ index, item, style, width }) => (
<View key={index} style={style}>
<Image source={{ uri: item.image }} style={styles.customImage} />
</View>
)}
/>
</View>
);
}
}
I solved it
componentDidMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then((response => this.setState({
isLoading: false,
data: response.data,
}))
)
}
And in render
if (this.state.isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View>
)
}
console.log(this.state.data)
return (
<View style={styles.container}>
<ImageSlider style={styles.viewPagerStyle}
loopBothSides
autoPlayWithInterval={6000}
images={this.state.data}
customSlide={({ index, item, style, width }) => (
<View key={index} style={[style]}>
<Image source={{ uri: item.image }} style={styles.customImage} />
</View>
)}
/>
</View>
);
}

Using parameter that would fetch data from another api based on a condition react native?

I have two pages Portfolio.js and PortfolioDetails.js. In the Portfolio.js file, I am fetching data from my api and displaying all the portfolios in a list. When I click on portfolio, it should take me to the PortfolioDetails screen, which will display only those stocks from stock api which are in the portfolio.
e.g if I click on Portfolio with id 1, it should filter out stocks from stock api which has portfolio id 1 and display all those stocks on the screen.
So far, I am successful in fetching both the apis and also when I click on one portfolio, it passes the portfolio id parameter to my PortfolioDetails screen. I am stuck where I have to filter the stocks to display based on this passed parameter - id.
Portfolio.js file
export default class Portfolio extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "Portfolio",
header: null,
};
};
constructor(props) {
super(props);
this.state = {
loading: true,
PortfolioSource: []
};
}
componentDidMount() {
fetch("http://127.0.0.1:8000/portfolios/")
.then(response => response.json())
.then((responseJson) => {
this.setState({
loading: false,
PortfolioSource: responseJson
})
})
.catch(error => console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
}}
/>
);
}
renderItem = (data) =>
<TouchableOpacity style={styles.list} onPress={() => this.props.navigation.push('Details', { portid: data.item.id })} >
<Text style={styles.lightText}>{data.item.id}</Text>
<Text style={styles.lightText}>{data.item.portfolio_id}</Text>
<Text style={styles.lightText}>{data.item.name}</Text>
<Text style={styles.lightText}>{data.item.description}</Text>
<Text style={styles.lightText}>{data.item.gains}</Text></TouchableOpacity>
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.PortfolioSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>
</View>
)
}
}
PortfolioDetails.js
export default class PortfolioDetails extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "PortfolioDetails",
header: null,
};
};
constructor(props) {
super(props);
this.state = {
loading: true,
PortfolioDetailsdataSource: [],
};
}
componentDidMount() {
fetch(`http://127.0.0.1:8000/stocks/`)
.then(response => response.json())
.then((responseJson) => {
this.setState({
loading: false,
PortfolioDetailsdataSource: responseJson
})
})
.catch(error => console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
}}
/>
);
}
goToPrevScreen = () => {
this.props.navigation.goBack();
}
renderItem = (data) =>
<TouchableOpacity style={styles.list}>
<Text style={styles.lightText}>{data.item.id}</Text>
<Text style={styles.lightText}>{data.item.ticker}</Text>
<Text style={styles.lightText}>{data.item.price}</Text>
<Text style={styles.lightText}>{data.item.market_cap}</Text>
<Text style={styles.lightText}>{data.item.YTD}</Text>
<Text style={styles.lightText}>{data.item.OneYear}</Text>
<Text style={styles.lightText}>{data.item.TwoYear}</Text>
<Text style={styles.lightText}>{data.item.TTM_Sales_Growth}</Text>
<Text style={styles.lightText}>{data.item.PE_Ratio}</Text>
</TouchableOpacity>
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.PortfolioDetailsdataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>
<Text> portid: {this.props.navigation.state.params.portid} </Text>
<Button
onPress={() => this.goToPrevScreen()}
title="go back to Portfolio"
/>
</View>
)
}
}
You can use .find(). For example:
PortfolioDetailsDataSource.find(item => item.id === this.props.navigation.state.params.portId)
Assuming IDs are unique, this will return the desired object, otherwise it will return the first occurrence that passes the condition.

React Native FlatList Touchable Opacity

I used FlatList to display the title that is in the data. I added in TouchableOpacity for the FlatList. So for example, when I click 'First', I want to show the the data from 'mood' and it will show the list of passionate,rousing and confident. I want to show the list on a new file/screen and show purely the mood of list.
This is my data:
const Mock =
[
{ title: 'First',
mood:
[
{name: 'passionate'},
{name: 'rousing'},
{name: 'confident'},
],
},
{ title: 'Second',
mood:
[
{name: 'rollicking'},
{name: 'cheerful'},
{name: 'fun'},
{name: 'sweet'},
{name: 'amiable'},
{name: 'natured'}
],
This is my FlatList code:
export default class Cluster1 extends Component{
render() {
return (
<View>
<FlatList
data={Mock}
renderItem={({ item, index }) => {
return <FlatListItem item={item} index={index} />;
}}
/>
</View>
);
}
}
class FlatListItem extends Component {
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity>
<Text style={styles.itemTitle}>{this.props.item.title}</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
What should I do with the TouchableOpacity when I want to show the mood name when I click the title?
This is my style code
const styles = StyleSheet.create({
itemTitle:{
fontSize: 25,
fontWeight: 'bold',
color: 'white',
margin: 20,
},
},
list:{
flex: 1,
backgroundColor: '#00BCD4',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
},
});
You should modify your code as below to do this:
export default class Cluster1 extends Component {
render() {
return (
<View style={{ margin: 30, backgroundColor: '#ddd' }}>
<FlatList
data={Mock}
renderItem={({ item, index }) => {
return <FlatListItem item={item} index={index} />;
}}
/>
</View>
);
}
}
class FlatListItem extends Component {
state = { showItemIndex: [false, false] };
_onPress = index => () => {
let showItemIndex = this.state.showItemIndex;
showItemIndex[index] = !this.state.showItemIndex[index];
this.setState({ showItemIndex });
};
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity onPress={this._onPress(this.props.index)}>
<Text style={styles.itemTitle}>{this.props.item.title}</Text>
</TouchableOpacity>
{this.state.showItemIndex[this.props.index] && (
<FlatList
data={this.props.item.mood}
extraData={this.state.showItemIndex}
renderItem={({ item, index }) => {
return (
<Text item={item} index={index}>
{item.name}
</Text>
);
}}
/>
)}
</View>
</View>
);
}
}
Use this, it's should be work fine for you:
Link: https://github.com/oblador/react-native-collapsible
Link: https://github.com/naoufal/react-native-accordion
Link: https://github.com/cuiyueshuai/react-native-expandable-section-flatlist
You could set a state variable which gets updated whenever your TouchableOpacity gets pressed. And then you conditionally render the title or the list of mood names:
class FlatListItem extends Component {
constructor(props) {
super(props);
this.state = {collapsed: true}
}
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity
onPress={this.onPress}
>
<Text style={styles.itemTitle}>
{this.props.item.title}
</Text>
</TouchableOpacity>
{this.state.collapsed ?
<View /> :
<View>
{this.props.item.mood.map(mood => <Text>{mood.name}</Text>)}
</View>}
</View>
</View>
);
}
onPress = () => {
this.setState({collapsed: !this.state.collapsed})
}
}
You can do this by separating out the datasource that is required to show in the list.
First to display title: you can do something like this
export default class Cluster1 extends Component {
state = {
data: []
};
componentWillMount() {
const titles = Mock.forEach(data => data.title);
this.setState({
data: titles
});
}
onItemClick = item => {
const itemIndex = Mock.findIndex(data => (
data.title === item
));
const values = Mock[itemIndex].mood;
this.setState({
data: values
});
};
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item, index }) => {
return (
<FlatListItem
item={item}
index={index}
onItemClick={this.itemClick}
/>
);
}}
/>
</View>
);
}
}
and then in your FlatlistItem code:
class FlatListItem extends Component {
render() {
return (
<View style={styles.list}>
<View>
<TouchableOpacity onPress={() =>
this.props.onItemClick(this.props.item)
}>
<Text style={styles.itemTitle}>
{
this.props.item.name ?
this.props.item.name : this.props.item
}
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}

search box function using redux in react native

I am trying to make a search in my db items but all I receive is the following error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Here is my code where I am creating the search page. JobItem I am using it in jobs page and everything is displaying correctly but here when I put the first letter in the search box I am getting the error.
import JobItem from './JobItem';
const { width, height } = Dimensions.get('window')
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {
text: '',
data: ''
}
}
static navigationOptions = {
headerVisible: false
}
filter(text) {
const data = this.props.jobs;
console.log(data);
const newData = data.filter(function (job) {
const itemData = job.title.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData) > -1
})
this.setState({
data: newData,
text: text,
})
}
deleteData() {
this.setState({ text: '', data: '' })
}
_renderJobs(job) {
return this.props.jobs.map((job) => {
return (
<JobItem
key={job._id}
title={job.title}
shortDescription={job.shortDescription}
logo={job.avatar}
company={job.company}
id={job._id}
city={job.location[0].city}
postDate={job.postDate}
dispatch={this.props.dispatch}
onPress={() => this.onJobDetails(job)}
/>
)
})
}
render() {
const { goBack } = this.props.navigation
return (
<View style={styles.container}>
<View style={styles.header}>
<FontAwesome
name="magnify"
color="grey"
size={20}
style={styles.searchIcon}
/>
<TextInput
value={this.state.text}
onChangeText={(text) => this.filter(text)}
style={styles.input}
placeholder="Search"
placeholderTextColor="grey"
keyboardAppearance="dark"
autoFocus={true}
/>
{this.state.text ?
<TouchableWithoutFeedback onPress={() => this.deleteData()}>
<FontAwesome
name="clock-outline"
color="grey"
size={20}
style={styles.iconInputClose}
/>
</TouchableWithoutFeedback>
: null}
<TouchableWithoutFeedback style={styles.cancelButton} onPress={() => goBack()}>
<View style={styles.containerButton}>
<Text style={styles.cancelButtonText}>Cancel</Text>
</View>
</TouchableWithoutFeedback>
</View>
<ScrollView>
<FlatList
style={{ marginHorizontal: 5 }}
data={this.state.data}
numColumns={3}
columnWrapperStyle={{ marginTop: 5, marginLeft: 5 }}
renderItem={({ job }) => this._renderJobs(job)}
/>
</ScrollView>
</View>
)
}
}
Please anyone help me with this.