Firebase + React Native - Grab each Document ID - javascript

I have been stuck on this for ages trying to figure out how I can console log each Firebase Cloudstore document ID separately when I press onto each rendered FlatList item.
I can grab a certain key / id by using onPress={() =>{console.log(this.state.posts[0].key)}} etc. But I dont know how to grab each one separately. In essence I only want the document ID of the touchableOpacity I have pressed. Not just [0]
Screenshots are below of App layout so you can get an understanding and also code example
PostsLayout.js
export default class PostsLayout extends React.Component {
render() {
const {summary, stringTime, user} = this.props;
return (
<TouchableOpacity
style={styles.container}
onPress={this.props.onPress}
>
<PostsUser user={user}/>
<PostsSummary summary={summary}/>
<PostsDate time={stringTime}/>
</TouchableOpacity>
)
}
}
FlatListLayout.js
export default class FlatListLayout extends React.Component {
render() {
return (
<ScrollView >
<FlatList
data={this.props.data}
renderItem={({item}) => <PostsLayout {...item} onPress={this.props.onPress}/>}
/>
</ScrollView>
)
}
}
ScreenLayout.js
export default class ScreenLayout extends React.Component {
state = {
posts: []
}
db = firebase.firestore()
path = this.db.collection('usersData').doc(firebase.auth().currentUser.uid).collection("posts")
onCollectionUpdate = (querySnapshot) => {
const posts = [];
querySnapshot.forEach((doc) => {
const {summary, time, stringTime, user, userId} = doc.data();
posts.push({
key: doc.id, doc, summary,
time, stringTime, user, userId
});
});
this.setState({
posts
});
}
componentDidMount() {
const {currentUser} = firebase.auth();
this.setState({currentUser})
this.unsubscribe = this.path.onSnapshot(this.onCollectionUpdate)
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<FlatListLayout
data={this.state.posts}
onPress={() => {console.log(this.state.posts[0].key)}}
/>
)
}
}
Thank you for reading this and please help :)

So the easiest fix would be send a function argument from the original press event in the child level.
For example, PostsLayout has the main onPress, so on this call just send back any data you need, each component will have specific data related to the component. As each react child is unique.
PostsLayout.js
export default class PostsLayout extends React.Component {
handleOnPress = () => {
const { onPress, index } = this.props;
if( typeof onPress === 'function') {
onPress(this.props, index); // here pass anything you want in the parent level, like even userm stringtime etc
}
}
render() {
const {summary, stringTime, user} = this.props;
return (
<TouchableOpacity
style={styles.container}
onPress={this.handleOnPress}
>
<PostsUser user={user}/>
<PostsSummary summary={summary}/>
<PostsDate time={stringTime}/>
</TouchableOpacity>
)
}
}
FlatListLayout.js
export default class FlatListLayout extends React.Component {
render() {
return (
<ScrollView >
<FlatList
data={this.props.data}
renderItem={({item, index }) => <PostsLayout {...item} index={index} onPress={this.props.onPress}/>}
/>
</ScrollView>
)
}
}
ScreenLayout.js
export default class ScreenLayout extends React.Component {
state = {
posts: []
}
db = firebase.firestore()
path = this.db.collection('usersData').doc(firebase.auth().currentUser.uid).collection("posts")
onCollectionUpdate = (querySnapshot) => {
const posts = [];
querySnapshot.forEach((doc) => {
const {summary, time, stringTime, user, userId} = doc.data();
posts.push({
key: doc.id, doc, summary,
time, stringTime, user, userId
});
});
this.setState({
posts
});
}
componentDidMount() {
const {currentUser} = firebase.auth();
this.setState({currentUser})
this.unsubscribe = this.path.onSnapshot(this.onCollectionUpdate)
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<FlatListLayout
data={this.state.posts}
onPress={(data, index) => {console.log(data); console.log(this.state.posts[index].key)}}
/>
)
}
}
Let me know if this doesn't make any sense :)

Related

set state twice in the same component

I am trying to setState to an event category for display inside of handleCategoryChange. The categories are rendered from the getCategories fetch point. I need to send a different value to the action fetch call in createEventHandler. The set state only happens once though and omits the second to send the first value of the state. Is there a work-around for this? or is this a limitation of react?
//... styles and imports
class NewEvent extends Component {
constructor(props) {
super(props);
this.state = {
event: {
category: ''
}
};
this.createEventHandler = this.createEventHandler.bind(this);
this.handleCategoryChange = this.handleCategoryChange.bind(this);
}
handleCategoryChange(evnt) {
this.setState({
event: {
...this.state.event,
category: evnt.target.value
}
});
}
componentWillMount() {
this.props.getCategories();
}
renderStepOne() {
const { event } = this.state;
const { categories } = this.props;
return (
<div style={styles.flexColumn}>
<Typography variant="title">Event</Typography>
<Select
value={event.category}
onChange={this.handleCategoryChange}
error={categoryError.length > 0}
>
{categories.map(category => (
<MenuItem key={category.id} value={category.name}>
{category.name}
</MenuItem>
))}
</Select>
</div>
);
}
createEventHandler() {
const { event } = this.state;
if (!error) {
let categoryId = this.props.categories.filter(e => {
if (e.name === event.category) {
return e;
}
});
categoryId = categoryId[0].id;
this.setState({
event: {
...event,
category: categoryId
}
});
this.props.createEvent(event, this.props.history);
}
}
render() {
const { step } = this.state;
const { isFetching, user, categories } = this.props;
return (
<ViewContainer title="New Event" isFetching={isFetching}>
<Paper style={styles.paper}>
<div style={styles.body}>{this.renderStepOne()}</div>
<MobileStepper
type="dots"
steps={0}
position="static"
nextButton={
<Button
variant="raised"
color="primary"
onClick={this.createEventHandler}
disabled={isFetching}
>
Submit
<KeyboardArrowRight />
</Button>
}
/>
</Paper>
</ViewContainer>
);
}
}
const mapStateToProps = state => ({
categories: state.events.categories
});
const mapDispatchToProps = dispatch => ({
createEvent: (event, history) => dispatch(createEvent(event, history)),
getCategories: () => dispatch(getCategories())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(NewEvent));
You could try using functional setState like so:
this.setState(() => ({
event: {
...this.state.event,
category: evnt.target.value
})
});
So that everything involving a setting of state happens together.

React Native - Passing fetch data to Modal and using StackNavigator with Modal

In here I have two problems. First is I'm trying to fetch dome data from my api and then pass this data to modal upon tapping a button. I have tried to use "state" and then declare that state like;
constructor(props){
super(props)
this.state = {
tbl: [],
tbl_no: null,
}
}
fetchTblOccpd = async () => {
const response = await fetch('http://192.168.***.***:****/PndngVRoutes/Occupied/');
const json = await response.json();
this.setState({ tbl: json })
this.setState({ tbl_no: json })
}
render() {
return (
.....
<PndModal
modalVisible = { this.state.modalVisible }
setModalVisible = { (vis) => { this.setState({ modalVisible: vis }) }}
tbl_no = { this.state.tbl_no }
/>
)
}
But this didn't work. I'm targeting to fetch a data and pass it to my Modal.
Sample
My Second question is after passing some data to Modal, I'm targeting to navigate to another screen/view from my modal.
here's my code
export default class PndModal extends Component {
constructor(props) {
super(props);
this.state = {
pnd_Data: [],
modalVisible: props.modalVisible,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
modalVisible: nextProps.modalVisible,
tbl_no: nextProps.tbl_no, //This is the data I'm trying to pass.
})
}
fetchOrdered = async () => {
const response = await fetch("http://192.168.254.***:****/PndngVRoutes/PendingView/" + this.state.tbl_no);
const json = await response.json();
this.setState({ pnd_Data: json })
}
componentDidMount() {
this.fetchOrdered();
}
_onPressItem = () => {
this.setState({
modalVisible: false,
});
}
render() {
return (
<Modal>
<View>
<View>
<View>
<Text>Table No: { this.state.tbl_no }</Text>
<FlatList
data = {this.state.pnd_Data}
numColumns = { 2 }
keyExtractor={(item, index) => index.toString()}
renderItem = {({ item }) =>
<View>
<Text>{ item.menu_name }</Text>
</View>
}
/>
<TouchableOpacity
onPress = { () => this.props.navigation.navigate('pend') }> // This is my navigation code
<Text>Add Order</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
)
}
}
Hello Am not too use to react native but i think is must be the same as react , for the solution to your problem , i think you should use reusable component . then create then pass your data as a props.
Here is an example in react.
//My reusable component
const Modal=(props) =>{
return (
<div>
{props.data}
</div>
);
}
}
Then you call your reusable component then pass the results from the api to it.
<Modal
data ={this.state.pnd_Data}
/>
for the second question you can just add link that will navigate you .... there will be no problem for that .
you can read about reusable component here
https://itnext.io/react-component-class-vs-stateless-component-e3797c7d23ab

onPress not getting called , throwing an error

I have following code where onPress i m expecting to call a function.
class BarButton extends Component {
render() {
const {imageUri,onPress} = this.props;
return (
<TouchableOpacity style={styles.buttonStyle}
onPress={() => onPress()}
>
<Image source={imageUri} style = {styles.buttonStyle}/>
</TouchableOpacity>
);
}
}
BarButton.propTypes = {
onPress: PropTypes.func.isRequired,
imageUri:PropTypes.string.isRequired
};
export default class ShopScreen extends Component {
static navigationOptions = {
title: 'Shop',
headerRight: <BarButton imageUri={require('./images/filter.png')} onPress={ this.onPressButton}/>,
headerTintColor:'black'
};
constructor(props){
super(props);
this.state ={ isLoading: true}
this.onPressButton = this.onPressButton.bind(this);
}
onPressButton()
{
this.props.navigation.navigate('Filter');
}
So I want to call the function onPressButton and navigate to next screen , in this I get error
You can use the navigation object that navigationOptions receive when you use a function instead of an object.
static navigationOptions = ({ navigation }) => {
return {
title: 'Shop',
headerRight: (
<BarButton
imageUri={require('./images/filter.png')}
onPress={() => navigation.navigate('Filter')}
/>
),
headerTintColor:'black'
};
};
Basically as a newbie i failed to understand that navigationOptions is a static var so can't reference anything using "this", following thread proposes many solutions here is the original link and out of all the on worked for me is as follows, posting it here for ease (with due credit to original author https://github.com/jacse)
https://github.com/react-navigation/react-navigation/issues/145
class MyScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerRight: <Button title="Save" onPress={() => params.handleSave()} />
};
};
_saveDetails = () => {
console.log('clicked save');
console.log('clicked save ' + this.state.xxxxxx);
}
componentDidMount() {
this.props.navigation.setParams({ handleSave: this._saveDetails });
}
render() {
return (
<View />
);
}
}

React Native - How to append to parent state array while in child component (StackNavigators)?

My project is looping through a data array in a child component Main, and I'm trying to update the state in parent component, App, on an event (swiping right on a card in Main), so that I could access the data that was 'swiped right' on a sibling Component in Favorites. Hopefully that makes sense?
The project structure is as such:
App
|__ Rootstack
|
|__Favorites
|__Main
In my Main component, I am mapping the collection array and looping thru:
collection = imagedata;
// a local JSON array of data that I am looping thru in Main
class Main extends React.Component {
_toFavs = () => {
this.props.navigation.navigate('Favorites');
};
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
......
</Card>
)
});
return (
<View>
<CardStack
onSwiped={() => {console.log('onSwiped')}
onSwipedRight={() => console.log('onSwipedLeft')}>
//
//HERE IS THE PART - HOW TO UPDATE THE 'favoritesList' array in the parent 'App's state?
//
{contents}
</CardStack>
</View>
);
}
}
const RootStack = StackNavigator(
{
Main: {
screen: Main},
Favorites: {
screen: Favorites}
},
{
initialRouteName: 'Main'
}
);
class Favorites extends React.Component {
// The plan is to eventually access the favoritesList array in App's state here and display cards that were swiped right in the Main component.
_onPress = () => {
this.props.navigation.navigate('Main');
};
render() {
return (
<View><Text>Hello!</Text></View>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
render() {
return <RootStack />;
}
}
I've come across some other answers of updating state such as
this.setState({ favoritesList: [...this.state.favoritesList, 'new value'] }), but how can I do this to the .state of App while i'm inside a child component Main?
Thanks in advance!
collection = imagedata;
// a local JSON array of data that I am looping thru in Main
class Main extends React.Component {
_toFavs = () => {
this.props.navigation.navigate('Favorites');
};
render() {
const contents = collection.map((item, index) => {
return (
<Card key={index}>
......
</Card>
)
});
return (
<View>
<CardStack
onSwiped={() => {console.log('onSwiped')}
onSwipedRight={() => {console.log('onSwipedLeft') ;
this.props.screenProps()}}>
//
{contents}
</CardStack>
</View>
);
}
}
const RootStack = StackNavigator(
{
Main: {
screen: Main},
Favorites: {
screen: Favorites}
},
{
initialRouteName: 'Main'
}
);
class Favorites extends React.Component {
// The plan is to eventually access the favoritesList array in App's state here and display cards that were swiped right in the Main component.
_onPress = () => {
this.props.navigation.navigate('Main');
};
render() {
return (
<View><Text>Hello!</Text></View>
);
}
}
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
updateArr=()=>{consol.log("fire") }
render() {
return <RootStack screenProps={this.updateArr} />;
}
}
i hope it solve your problem
update props-name

React.JS - multiple elements sharing a state ( How do I modify only one of the elements without affecting the others? )

class App extends Component {
constructor(props) {
super(props);
this.state = { Card: Card }
}
HandleEvent = (props) => {
this.SetState({Card: Card.Active}
}
render() {
return (
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
<Card Card = { this.state.Card } HandleEvent={
this.handleEvent }/>
)
}
}
const Card = props => {
return (
<div style={props.state.Card} onClick={
props.HandleEvent}>Example</div>
)
}
Every time I click on one of the cards all of my elements change states, how do I program this to only change card that I clicked?
Here's a working example
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
0: false,
1: false
};
}
handleEvent(idx) {
const val = !this.state[idx];
this.setState({[idx]: val});
}
render() {
return (
<div>
<Card state={this.state[0]} handleEvent={()=>this.handleEvent(0) } />
<Card state={this.state[1]} handleEvent={()=>this.handleEvent(1) } />
</div>
);
}
}
const Card = (props) => {
return (<div onClick={() => props.handleEvent()}>state: {props.state.toString()}</div>);
}
You can also see it in action here
Obviously this is a contrived example, based on your code, in real world application you wouldn't store hardcoded state like {1: true, 2: false}, but it shows the concept
It's not completely clear from the example what is the Card in the constructor. But here the example of how you can modify clicked element.
Basically you can keep only index of clicked element in parent's state, and then pass it as some property to child component, i.e. isActive here:
const cards = [...arrayOfCards];
class App extends Component {
constructor(props) {
super(props);
this.state = { activeCardIndex: undefined }
}
HandleEvent = (index) => {
this.SetState({
activeCardIndex: index
});
}
render() {
return ({
// cards must be iterable
cards.map((card, index) => {
return (
<Card
key={index}
Card={Card}
isActive={i === this.state.activeCardIndex}
HandleEvent={this.HandleEvent.bind(this, index)}
/>
);
})
});
}
}
const Card = props => {
// style active card
const style = Object.assign({}, props.Card, {
backgroundColor: props.isActive ? 'orange' : 'white',
});
return (
<div style={style} onClick={
props.HandleEvent}>Example</div>
)
}

Categories

Resources