How do I get data from firebase realtime databese with React Native? - javascript

I'm trying to get a data firebase realtime database. I can show data with console.log() but I cannot show in Flat list. I tried different ways to solve this problem. But, I could not find any certain solution.
Here, my JSON tree in firebase:
Here, my code:
const NotesList = (props) => {
const { colors } = useTheme();
const styles = customStyles(colors);
const user = auth().currentUser
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const getData = () => {
firebase.database().ref(`notes/`).on('value', function (snapshot) {
console.log(snapshot.val())
});
}
useEffect(() => {
setTimeout(() => {
setData(getData);
setLoading(true);
}, 1000);
}, []);
const renderItem = ({ item }) => {
return (
<View style={{ marginRight: 10, marginLeft: 10 }}>
<TouchableOpacity>
<NoteCard title={item.noteTitle} icerik={item.noteDetails} date={item.timestamp} />
</TouchableOpacity>
</View>
);
};
const split = () => {
let icerik = data.map((item, key) => item.icerik);
return icerik.slice(0, 1) + '...';
};
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<NoteSearchBar />
{!loading ? (
<View style={{ alignItems: 'center' }}>
<ActivityIndicator color="#ff5227" />
</View>
) : (
<FlatList
data={data}
numColumns={2}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
/>
)}
<TouchableOpacity
style={styles.addButton}
onPress={() => props.navigation.navigate('AddNote')}>
<Plus width={40} height={40} fill="white" margin={10} />
</TouchableOpacity>
</View>
);
};
I tried many different ways, but no suggestions solved the problem. I would be glad if you could help.

Your getData doesn't return the data, as the data is loaded asynchronously. You should instead move he call to setData into your getData:
const getData = () => {
firebase.database().ref(`notes/`).on('value', function (snapshot) {
setData(snapshot.val());
});
}
useEffect(() => {
setTimeout(() => {
getData()
setLoading(true);
}, 1000);
}, []);
Now the call to setData will put the JSON into the state, which in turn forces React to repaint the UI with the new data.

Ciao, basically you made 2 mistake:
getData does not return nothing so data will be never updated with values from firebase;
useEffect has a strange implementation (it's useless call setTimeout you are already waiting data from firebase);
My suggestion is to call getData from useEffect like this:
useEffect(() => {
getData()
}, []);
And modify getData like this:
const getData = () => {
firebase.database().ref(`notes/`).on('value', function (snapshot) {
setData(snapshot.val());
setLoading(true);
});
}

I found solution and I want to share it with you guys.
I kept the data from firebase as a child at a new value, then I put it in setData. In this way, I can display the data in the flat list.
const getData = () => {
firebase.database().ref(`notes/`).on('value', snapshot => {
let responselist = Object.values(snapshot.val())
setData(responselist)
console.log(snapshot.val())
setLoading(true);
});
}
useEffect(() => {
getData();
}, []);
Also, while firebase realtime DB was working, a new method called Object method helped when taking child values in the JSON tree.

Related

How to add navigation to different items in a rendered array

I am attempting to press on this pressable button, and navigate to a new page. The tricky bit is that this Pressable item is part of a returned array, as there are multiple of them being rendered each with different data. I want each button to take me to a 'product page', each page being different depending on the button
Here is what i have so far:
The main function
const JobRequestList = () => {
const [data, setData] = useState([]);
useEffect(() => {
returnArray().then(data2 => {
setData(data2);
});
}, []);
if (data.length === 0) {
j = [];
return (
<ScrollView>
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
</ScrollView>
);
} else {
return <ScrollView>{data}</ScrollView>;
}
};
This requests the data, and returns it in a form that can be rendered. It either returns a no object, or an array of items from the below function - This is where my onPress is located, and have no idea how to implement a navigation fnction into it. Please note, i already have my navigation functions setup
const returnArray = async () => {
return queryData().then(() => {
return j.map(x => {
return (
<Pressable
key={x.id}
style={styles['wrapper' + x.data().PD]}
onPress={() => {}}>
<Text style={styles.text}> {x.data().PD} </Text>
<Text style={styles.text}> {x.data().address} </Text>
<Text style={styles.text}> {x.data().time} </Text>
</Pressable>
);
});
});
};
The above function then calls the below
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
j.push(doc);
});
});
};
Here is what my navigation functions should be inside this class - Again, which is already setup correctly
const navigation = useNavigation();
navigation.navigate('JobInfo');
Thankyou in advanced!
It is anti-pattern in React to store JSX in component state. React components's rendered UI is a function of state & props. Store the data in state and then render the data mapped to JSX.
Example:
queryData fetches firebase docs & data
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
const docs = [];
querySnapshot.forEach(doc => {
docs.push({
...doc,
data: doc.data(),
});
});
return docs;
});
};
Apply the navigation logic in the Pressable component's onPress handler when mapping the data state.
const JobRequestList = () => {
const navigation = useNavigation();
const [data, setData] = useState([]);
useEffect(() => {
queryData()
.then(data => {
setData(data);
});
}, []);
return (
<ScrollView>
{data.length
? data.map(el => (
<Pressable
key={el.id}
style={styles['wrapper' + el.data.PD]}
onPress={() => {
navigation.navigate('JobInfo');
}}
>
<Text style={styles.text}> {el.data.PD} </Text>
<Text style={styles.text}> {el.data.address} </Text>
<Text style={styles.text}> {el.data.time} </Text>
</Pressable>
))
: (
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
)
}
</ScrollView>
);
};

useState is not working properly in React Native

I'm working on an application and using WordPress API for showing posts. I've created 2 buttons to navigate the list of posts. As you know there is an argument "page=" to get posts on a specific page, I've initialized a state to maintain page number. The main problem is that it's not incrementing correctly.
Post Screen Code -
import React, { useState, useEffect } from "react";
import { View, FlatList, TouchableOpacity } from "react-native";
import { Colors } from "../constant/colors";
import globalStyles from "../constant/globalStyle";
import axios from "axios";
import PostCard from "../components/PostCard";
import CustomButton from "../components/Button";
const Updates = () => {
const [data, setData] = useState([]);
const [page, setPage] = useState(1);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await axios.get(
`https://bachrasouthpanchayat.in/wp-json/wp/v2/posts?embed=true&page=${page}`
);
setData(response.data);
setLoaded(true);
};
const previousHandler = () => {
setLoaded(false);
let newPage = page - 1;
setPage(newPage);
fetchData();
};
const nextHandler = () => {
setLoaded(false);
let newPage = page + 1;
setPage(newPage);
fetchData();
};
return (
<View
style={{
...globalStyles.container,
backgroundColor: Colors.BACKGROUND_SCREEN,
}}
>
{loaded ? (
<>
<FlatList
style={{ flex: 1, margin: 10 }}
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => {
return (
<TouchableOpacity activeOpacity={0.7}>
<PostCard
title={item.title.rendered}
imageUrl={item.jetpack_featured_media_url}
/>
</TouchableOpacity>
);
}}
/>
<View
style={{
flexDirection: "row",
alignItems: "center",
alignContent: "stretch",
justifyContent: "center",
}}
>
{page == 1 ? (
<TouchableOpacity
activeOpacity={0.7}
style={{ width: "100%" }}
onPress={nextHandler}
>
<CustomButton>Next</CustomButton>
</TouchableOpacity>
) : (
<>
<TouchableOpacity
activeOpacity={0.7}
style={{ marginRight: 2, width: "50%" }}
onPress={previousHandler}
>
<CustomButton>Previous</CustomButton>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
style={{ width: "50%" }}
onPress={nextHandler}
>
<CustomButton>Next</CustomButton>
</TouchableOpacity>
</>
)}
</View>
</>
) : null}
</View>
);
};
export default Updates;
I had logged state in every state and found that was not incrementing from 1 to 2 on pressing the button the first time. I think state updated after API call because both buttons had started showing even I've used condition to show both buttons only if the state is not 1
Please let me know if i've made any silly mistake 😂😂
useEffect(() => {
fetchData();
}, []);
The last argument to useEffect is an array of dependencies so that React will only re-run the effect when the dependencies have changed. You are passing an empty array, which tells React that there are no dependencies and the effect should only be run once, when the component is first mounted.
Now you actually want the effect to re-run when the page changes, so you should put page in the depenency array:
useEffect(() => {
fetchData();
}, [page]);
And (credit: #Keith) you should remove the extra fetchData() calls in the nextHandler and previousHandler
You have to implement it like this:
useEffect(() => {
const fetchData = async () => {
setLoaded(true);
const response = await axios.get(
`https://bachrasouthpanchayat.in/wp-json/wp/v2/posts?embed=true&page=${page}`
);
setData(response.data);
setLoaded(false);
};
fetchData();
}, [page]);
const previousHandler = () => {
setPage(prevPage => prevPage - 1);
};
This way whenever the user changes the page, it will automatically call the function in useEffect, since it is in the dependency array.

Re rendering a component with an async function inside

I am new to react native and my JS is a bit rusty. I need to be able to change the value of my collection for the firestore. I have two buttons that will change the value of typeOfPost by setting the state. Component1 can successfully get "this.state.typeOfPost". However, when I click one of the buttons and update the state my log inside of the async function is not being called. It is only called when the app initially renders. What I find weird is that my log on the top of Component1 will display as expected. Is there any better way of doing this?
class Forum extends Component {
state = {
typeOfPost: ' '
}
onPressSitter = () => {
this.setState({
typeOfPost: 'sitterPosts'
})
}
onPressNeedSitter = () => {
this.setState({
typeOfPost: 'needPosts'
})
}
render() {
return (
<View style={styles.container}>
<View style={styles.row}>
<TouchableOpacity
style={styles.button}
onPress={this.onPressSitter}
>
<Text>I am a sitter</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={this.onPressNeedSitter}
>
<Text>Need a sitter</Text>
</TouchableOpacity>
</View>
<View>
<Component1 typeOfPost = {this.state.typeOfPost}> </Component1>
</View>
</View>
)
}
}
const Component1 = (props) => {
console.log("type of post " + props.typeOfPost);
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [data, setData] = useState([]); // Initial empty array of data
const getData = async () => {
console.log("type of post inside async " + props.typeOfPost);
const subscriber = firestore()
.collection(props.typeOfPost) // need to be able to update this
.onSnapshot(querySnapshot => {
const data = [];
querySnapshot.forEach(documentSnapshot => {
data.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setData(data);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}
useEffect(() => {
getData();
}, [])
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
data={data}
ListEmptyComponent={
<View style={styles.flatListEmpty}>
<Text style={{ fontWeight: 'bold' }}>No Data</Text>
</View>
}
renderItem={({ item }) => (
<View>
<Text>User ID: {item.fullName}</Text>
</View>
)}
/>
)
}
There is a difference between mount and render. I see no problem with your code except the few remarks I have made. The thing is that when you change typeOfPost, the component is rerendered, but the useEffect is not called again, since you said, it's just called when it was first mounted:
useEffect(() => {
}, []) // ---> [] says to run only when first mounted
However here, you want it to run whenever typeOfPost changes. So here is how you can do this:
useEffect(() => {
getData();
}, [typeofPost])
class Forum extends Component {
state = {
typeOfPost: ' '
}
onPressSitter = () => {
this.setState({
typeOfPost: 'sitterPosts'
})
}
onPressNeedSitter = () => {
this.setState({
typeOfPost: 'needPosts'
})
}
render() {
return (
<View style={styles.container}>
<View style={styles.row}>
<TouchableOpacity
style={styles.button}
onPress={this.onPressSitter}
>
<Text>I am a sitter</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={this.onPressNeedSitter}
>
<Text>Need a sitter</Text>
</TouchableOpacity>
</View>
<View>
<Component1 typeOfPost = {this.state.typeOfPost}> </Component1>
</View>
</View>
)
}
}
const Component1 = (props) => {
const { typeOfPost } = props
console.log("type of post " + props.typeOfPost);
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [data, setData] = useState([]); // Initial empty array of data
const getData = () => {
setLoading(true)
console.log("type of post inside async " + props.typeOfPost);
const subscriber = firestore()
.collection(props.typeOfPost) // need to be able to update this
.onSnapshot(querySnapshot => {
const data = [];
querySnapshot.forEach(documentSnapshot => {
data.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setData(data);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}
useEffect(() => {
getData();
}, [typeofPost])
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
data={data}
ListEmptyComponent={
<View style={styles.flatListEmpty}>
<Text style={{ fontWeight: 'bold' }}>No Data</Text>
</View>
}
renderItem={({ item }) => (
<View>
<Text>User ID: {item.fullName}</Text>
</View>
)}
/>
)
}
you are using a class based component to access react hook which is a bad practice, i will advice you use a functional component and you have access to react useCallback hook which will handle your request easily
const ButtonPressed = useCallback(() => {
setLoading(true);
getData()
}).then(() => setLoading(false));
}, [loading]);

How can I get an ID from a FlatList keyExtractor and use it in a modal?

I'm trying to take an ID from a keyExtractor in a FlatList and give that ID to a modal so that it can show the information from the API. The current implementation throws a warning - "Cannot update a component from inside the function body of a different component." The problem is that I can't think of another way to update that modal.
Here's the relevant code:
export default function AllList() {
const [isLoading, setLoading] = useState(true);
const [drinkData,setDrinkData] = useState([]);
const [searchValue, onChangeText] = useState(''); //needed for search
const [reloading, setReloading] = useState(false);
const [modalVisible, setModalVisible] = useState(false); //normal modal visibility handler
const [idDrink, setIdDrink] = useState(0);
const renderItem = ({ item }) => {
setIdDrink(item.id);
return (
<RowCard id={item.idDrink} image={item.strDrinkThumb} title={item.strDrink} alcontent={item.strAlcoholic}
ingredient1={item.strIngredient1} ingredient2={item.strIngredient2} ingredient3={item.strIngredient3} setModalVisible={setModalVisible}
/>
);
};
useEffect (() => {
fetch('https://www.thecocktaildb.com/api/json/v1/1/search.php?s=' + searchValue)
.then((response) => response.json())
.then((json) => setDrinkData(json.drinks))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
},[drinkData]);
return (
<View style = {styles.screen}>
<View style = {styles.searchSection}>
<TextInput
placeholder="Search Drinks..."
style={styles.input}
onChangeText={text => onChangeText(text)}
value={searchValue}/>
</View>
<FlatList
data={drinkData}
keyExtractor={({ idDrink }, index) => idDrink}
removeClippedSubviews={true}
initialNumToRender={5}
renderItem={renderItem}
extraData={reloading}
/>
<DrinkPopup modalVisible={modalVisible} setModalVisible={setModalVisible} drinkID={idDrink}/>
</View>
);
};
Another user helped me get this far, but I'm on a time crunch and need to figure this out as soon as I can.
Can you try this
keyExtractor={(item) => item. idDrink}
in flatlist drinkData is your array. so you have to pick the idDrink from every object.

How to sort a flatlist in react native [duplicate]

This question already has an answer here:
Sorting react-native FlatList
(1 answer)
Closed 2 years ago.
I am working on a to do list app in react native, when a new item is added it goes directly to the last place and I will like every new object to go to the first place. To achieve this I tried adding a function that is supposed to sort the items but it the code doesnt make any changes. How can I sort these items in my to do list?
app.js
const [todos, setTodos] = useState([]);
const [addMode, setAddMode] = useState(false);
const [isReady, setIsReady] = useState(false);
const addTodoHandler = addTodos => {
if (addTodos.lenght === 0) {
return;
};
setTodos(prevTodos => [...prevTodos, { key: Math.random().toString(), value: addTodos, date: Date.now() }]);
setAddMode(false);
Keyboard.dismiss();
};
const sortTodos = () => { //this is the function that is supposed to sort the items.
const todoSort = [...todos];
const soarted = todoSort.sort((a, b) => {
return a.todoSort - b.todoSort;
})
setTodos(soarted);
};
return (
<View style={styles.screen}>
<Header />
<AddTodo onAddTodo={addTodoHandler} />
<FlatList
keyExtractor={(item, index) => item.key}
data={ todos }
renderItem={({ item }) => <TodoItem key={item.key}
todoKey={item.key}
title={item.value}
editHandler={handleEdit}
pressHandler={pressHandler}/> }
/>
</View>
);
AddTodo.js
const AddTodo = props => {
const [text, setText] = useState('');
const changeHandler = (val) => {
setText(val);
};
const addTodoHandler = () => {
props.onAddTodo(text);
setText('');
};
return (
<View style={styles.inputView}>
<TextInput style={styles.textInput} placeholder='What do you want to do?' onChangeText={changeHandler} value={text}/>
<Buttons title="Add" onPress={addTodoHandler} style={styles.salsachBtn}/>
</View>
);
};
TodoItem.js
const TodoItem = props => {
return (
<View>
<View style={styles.items}>
<View style={styles.itemContainer}>
<Text style={styles.itemText}>{props.title}</Text>
</View>
</View>
</View>
);
};
if you have any questions please let me know in the comments:)
First idea:
Add your 'sortTodos' inside function that handle adding new item.
Add date to items with e.g. Date.now()
Sort a.date - b.date
Second (without sorting): you can try to use unshift
const newTodo = [...prevTodos]
newTodo.unshift({ key: Math.random().toString(), value: addTodos });
setTodos(newTodo)

Categories

Resources