I'm having issues rendering data from firebase into the FlatList component.
I only have success rendering a FlatList when I hard code an array into the data property. When I pass data in through the state FlatList doesn't render anything.
Any help would be appreciated.
loadBooks = () => {
this.setState({
refreshing: true,
book_feed: []
});
var that = this;
database
.ref('books')
.once('value')
.then(function(snapshot) {
const exists = snapshot.val();
if (exists) data = snapshot.val();
for (var book in data) {
var bookObj = data[book];
that.state.book_feed.push({
id: book,
name: bookObj.name,
url: bookObj.image,
price: bookObj.price
});
}
})
.catch(error => console.log(error));
that.setState({
refreshing: false,
loading: false
});
};
loadNew = () => {
this.loadBooks();
};
componentDidMount() {
this.loadNew();
}
render() {
<View>
{this.state.loading == true ? (
<Text>Loading...</Text>) : (
<FlatList
refreshing={this.state.refreshing}
onRefresh={this.loadNew}
data={this.state.book_feed}
renderItem={({ item }) =>
<Text style={{ flex: 1 }}>{item.name}</Text>}
/>)}
</View>
}
You are directly mutating the state which is a wrong code practice.
The correct code would look like this:
let book_feed = [];
database
.ref('books')
.once('value')
.then(function(snapshot) {
const exists = snapshot.val();
//let data = []; define data if not defined.
if (exists) data = snapshot.val();
for (var book in data) {
var bookObj = data[book];
book_feed.push({
id: book,
name: bookObj.name,
url: bookObj.image,
price: bookObj.price
});
}
})
.catch(error => console.log(error));
that.setState({
book_feed,
refreshing: false,
loading: false
});
Related
FlatList is not rendering data from state however it is working for the DATA variable. this.state.DATA is an array of objects just like the DATA variable.The DATA variable is just a dummy variable that was given in the reactnative docs. I want to display the contents of this.state.DATA.
import React, { Component } from 'react';
import { Text, View ,FlatList} from 'react-native';
import SectionHeader from '../SectionHeader';
import {TableHeader,TableHeaderText,IssueContainer} from './style';
import {CheckOutlined,InfoCircleOutlined,CaretDownOutlined} from '#ant-design/icons'
const DATA = [
{
id: '1',
title: "No show password eye button in Login form",
},
{
id: '2',
title: 'Second Item',
},
{
id: '3',
title: 'Third Item',
},
];
var repos=[],issues=[];
export default class App extends Component {
state={isLoading:true};
componentDidMount() {
fetch('https://api.github.com/orgs/anitab-org/repos')
.then((response)=>response.json())
.then((json)=> json.forEach(function(repo,idx){
repos.push(repo.name);
fetch('https://api.github.com/repos/anitab-org/'+repo.name+'/issues')
.then((response)=>response.json())
.then((json)=>json.forEach(function(issue,idx){
var flag=false;
var issue_tmp={
id:issue.id.toString(),
url:issue.html_url,
title:issue.title,
milestones:issue.milestones,
comments:issue.comments,
number:issue.number,
assignees:issue.assignees,
labels:[],
};
issue.labels.forEach(function(label){
if(label.name==="First Timers Only")
flag=true;
issue_tmp.labels.push({
id:label.id,
name:label.name,
color:label.color
})
})
if(flag===true && issue_tmp!=null)
issues.push(issue_tmp)
}));
}))
.then(()=>{
this.setState({
repos:repos,
DATA:issues,
isLoading:false,
});
})
}
render() {
if(this.state.isLoading===true)
return(<></>)
else{
return (
<View style={{alignItems: 'left',width:'80%'}}>
<SectionHeader title="SOME COOL FIRST-TIME ISSUES TO WORK ON"/>
<TableHeader>
<TableHeaderText style={{color:'#000',textAlign:'left'}}><InfoCircleOutlined /> 5 Open</TableHeaderText>
<Text style={{flex:6,color:'#586069'}}><CheckOutlined /> 45 Closed</Text>
<TableHeaderText>Author <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Label <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Milestone <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Assignee <CaretDownOutlined /></TableHeaderText>
<TableHeaderText>Sort <CaretDownOutlined /></TableHeaderText>
</TableHeader>
<FlatList
data={this.state.DATA}
renderItem={({item})=>(
<IssueContainer key={item.id}><Text>{item.title}</Text></IssueContainer>
)}
keyExtractor={item => item.id}
/>
</View>
);
}
}
};
The reason it doesn't work is because you have nested promises. The outer then won't wait the inner ones to execute the following code. This way last then with setState is executed without those promises being resolved:
.then((json)=> json.forEach(function(repo,idx){
// bunch of promises being executed here with some chained then blocks
// outer 'then' chain doesn't wait these promises to resolve
}))
.then(()=>{
// since the previous one doesn't wait its inner promises to execute
// this chained 'then' is executed without those promises return their values
this.setState({
repos:repos,
DATA:issues,
isLoading:false,
});
I rewrote your code with async/await because with some many promises it's a hard read. I use Promise.all to wrap all fetches. Also I abstracted your issue treatment to its own normalize function:
state = {
isLoading: true,
repos: [],
DATA: [],
};
async componentDidMount() {
const repos = [];
try {
const response = await fetch('https://api.github.com/orgs/anitab-org/repos');
const jsonData = await response.json();
const DATA = await Promise.all(jsonData.map(async ({ name }) => {
repos.push(name);
const issuesResponse = await fetch(`https://api.github.com/repos/anitab-org/${name}/issues`);
const issuesJSON = await issuesResponse.json();
const repoIssues = issuesJSON.map(this.normalizeIssue);
return repoIssues.filter(issue => issue !== undefined);
}))
// DATA needs to be flat since it's an array of arrays
this.setState({
repos,
DATA: DATA.flat(),
isLoading:false,
})
} catch (error) {
console.log(error);
}
}
normalizeIssue = (issue) => {
let flag = false;
const issueNormalized = {
id:issue.id.toString(),
url:issue.html_url,
title:issue.title,
milestones:issue.milestones,
comments:issue.comments,
number:issue.number,
assignees:issue.assignees,
labels:[],
};
issue.labels.forEach(function(label){
if(label.name === "First Timers Only") flag = true;
issueNormalized.labels.push({
id:label.id,
name:label.name,
color:label.color
})
})
if(flag === true && issueNormalized !== null) return issueNormalized
}
I am using Draftjs and Draftjs mention plugin. When there is a suggestion for mention name it renders fine but if suggestion doesn't exist and I use # followed by random text it crashes. Can anyone help me out with this. Will be very grateful. Thank you.
this.mentionMembersPlugin = createMentionPlugin({
entityMutability: "IMMUTABLE",
positionSuggestions,
mentionTrigger: "#",
mentionPrefix: "",
supportWhitespace: true
});
onChangeEditor = editorState => {
this.setState({ emptyField: false });
this.setState({ editorState });
};
onSearchMemberChange = ({ value }) => {
this.setState({
suggestionMembers: defaultSuggestionsFilter(
value,
this.state.mentionMembers
)
});
};
handleKeyCommand(command: string): DraftHandleValue {
if (command === "save_teamsync") {
// Perform a request to save your contents, set
// a new `editorState`, etc.
this.add_taskComment();
return "handled";
}
return "not-handled";
}
add_taskComment() {
let newData = convertToRaw(this.state.editorState.getCurrentContent());
let checkText = newData.blocks[0].text;
this.setState({ clicked: true });
var r = JSON.stringify(
convertToRaw(this.state.editorState.getCurrentContent())
);
//e.preventDefault();
if (checkText.trim().length !== 0) {
if (this.isValid(r)) {
var data = {};
data.text = r;
data.mentionMembers = r.entityMap;
this.props
.addTaskComment(this.props.task._id, this.showTags, data)
.then(res => {
if (res.data.success) {
const editorState = EditorState.push(
this.state.editorState,
ContentState.createFromText("")
);
this.setState({
editorState: EditorState.moveFocusToEnd(editorState)
});
//this.setState({ editorState: EditorState.moveFocusToStart(EditorState.createEmpty()), clicked:false });
this.lastComment.scrollIntoView({ behavior: "smooth" });
}
});
}
} else {
this.setState({ isCommentEmpty: true });
}
}
<Editor
blockStyleFn={"myBlockStyleFn"}
editorState={this.state.editorState}
onChange={this.onChangeEditor}
plugins={this.plugins}
handleKeyCommand={this.handleKeyCommand}
keyBindingFn={this.myKeyBindingFn}
placeholder="Write a comment"
ref={element => {
this.editor = element;
}}
/>
<MentionMembersSuggestions
onSearchChange={this.onSearchMemberChange}
suggestions={this.state.suggestionMembers}
/>
This are all the code that are being used to render the comments.
This is the error I am getting "Unhandled Rejection (TypeError): this.props.getEditorState is not a function".
I'm getting data from a payload which has a total number of likes on each post. On the user screen, there's an icon for the user to like a post and what i want to achieve is when the user taps on it, the value show be increased to plus 1 against that particular post
VIEW:
{
posts.map((item, i) => {
return (
<View key={i} style={styles.user}>
<Card>
<ListItem
titleStyle={{ color: '#36c', fontWeight:'500' }}
titleNumberOfLines={2}
hideChevron={false}
roundAvatar
title={item.headline}
avatar={{uri:'https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg'}}
/>
<Text style={{marginBottom: 10, fontSize:16, color:'#4a4a4a', fontFamily:'HelveticaNeue-Light'}}>
{item.text}
</Text>
<TouchableOpacity style={styles.likeContainer}>
<Text style={{fontSize:14}}>{item.likesCount}{"\n"}</Text>
<Icon
onPress={()=>onLikePost(item)}
name='md-thumbs-up'
type='ionicon'
iconStyle={[(item.isLiked=== true) ? styles.likedColor : styles.unLikedColor]}
/>
</TouchableOpacity>
</Card>
</View>
);
})
}
CONTAINER:
state = {
posts : [],
id: '',
user: ''
}
componentDidMount = () => {
const { navigation } = this.props;
this.setState({
id : navigation.getParam('id'),
user: navigation.getParam('user')
}, ()=> this.getData())
}
getData = () => {
const api = create({
baseURL: 'https://url.com/api',
headers: {'Accept': 'application/json'}
});
api.get('/groups/'+`${this.state.groupID}`+'/posts').then((response) => {
let data = response.data.data
this.setState({ posts: data });
console.log(JSON.stringify(this.state.posts))
})
}
onLikePost = (item) => {
item.likeCount = item.likeCount+1
}
You are storing posts data in state variable so use setState to update it. Use map and check for each post, whenever id (unique property of each post) matches to id of the clicked item, increase its likesCount otherwise return the same data.
Write it like this:
onLikePost = (item) => {
this.setState(prevState => ({
posts: prevState.posts.map(el => el.id === item.id? {...el, likesCount: el.likesCount+1} : el)
}))
}
Update: Put the check before updating the count value and change the isLiked bool also.
onLikePost = (item) => {
this.setState(prevState => ({
posts: prevState.posts.map(el => {
if(el.id === item.id) {
return {
...el,
isLiked: !el.isLiked,
likesCount: !el.isLiked? el.likesCount+1 : el.likesCount-1,
}
}
return el;
})
}))
}
Note: I am assuming each post has a key id unique value, if it doesn't exist then use any other unique property of the each post.
If array sequence is not an issue, you can use item index and use setState to update it.
<Icon
onPress={()=>onLikePost(i)}
...
/>
...
onLikePost = (i) => {
let posts = this.state.posts;
posts[i].likesCount = !posts[i].isLiked ? posts[i].likesCount + 1 : posts[i].likesCount - 1;
posts[i].isLiked = !posts[i].isLiked;
this.setState({ posts: posts})
}
I have set up a call to fetch data from my firebase database using react native.
Database structure
Code inside FirebaseList.js
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentWillMount() {
firebase.database().ref('/signposts/items').on('value', snapshot => {
const dataArray = [];
const result = snapshot.val();
for (const data in result) {
dataArray.push(data);
}
this.setState({ data: dataArray });
console.log(this.state.data);
});
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<Text>{item}</Text>
)}
keyExtractor={item => item}
/>
);
}
I believe the connection to firebase is successful as I can build and run the application. However, when the component renders, I do not see my two rows of data 'row1' and 'row2'.
You said your code is right then also check that this rules are set
{
"rules": {
"foo": {
".read": true,
".write": false
}
}
}
Note : - When you use the above rule your database is open for all Read more here. Make you use update the rules once you push to production.
If console.log(dataArray) shows an empty array (assuming that console.log() works...), try checking your connection:
componentDidMount() {
const ref = firebase.database().ref('/signposts');
const checkConnection = firebase.database().ref(`.info/connected`);
checkConnection.on('value', function(snapshot) {
if (snapshot.val() === true) { /* we're connected! */
firebase.database().ref('/signposts').on('value', snapshot => {
const dataArray = [];
const result = snapshot.val();
for (const data in result) {
dataArray.push(data);
}
if (dataArray.length === 0)
console.log("No data.")
else
this.setState({ listViewData: dataArray });
});
} else { /* we're disconnected! */
console.error("Check your internet connection.")
}
}
}
I have an array that is generated from a firebase database query.
I want to save that in state so that as the data changes, it will re-render the screen.
I can't seem to get the value from state into my function. If I put the value from the array, it works, but then it won't automatically re-render when data changes.
Screen shot of it using the array... note the console log is printing that the state is set correctly.
and here it is with the error
It's gotta be right around line 101, but I cannot figure out the right syntax to make thsi work.
UPDATE: I was not initializing state, that was one part of the error.
import React, { Component } from 'react';
import Flexbox from 'flexbox-react';
import firebaseApp from '../api/firebase';
import GeoFire from 'geofire';
var geoRef = firebaseApp.database().ref('shiftgeo');
var geoFire = new GeoFire(geoRef);
var ref = geoFire.ref(); // ref === firebaseRef
var shiftKeys = []; // this array will hold the keys from the geoFire results
var shifts = []; // this array will hold the actual shift data of shifts in the geo, then we will filter it later
console.log(firebaseApp);
export class App extends React.Component {
constructor() {
super();
this.state = {
fname: 'Chris',
lname: 'Chong',
cellphone: '503 830 4313',
email: 'chris#ehotleads.com',
dataSource: ''
};
}
componentWillMount() {
let email = 'chris#ehotleads.com';
let password = '123456789';
firebaseApp.auth().signInWithEmailAndPassword(email, password)
.then((data) => {
//this.setState({ error: 'Account already exists. Logging you in...', loading: false });
console.log('success data', data);
this.setState({
user: data,
});
})
.catch((data) => {
//this.setState({ error: 'Authentication failed.', loading: false });
console.log('error data', data);
});
}
componentDidMount() {
var geoQuery = geoFire.query({
center: [45.616422, -122.580453],
radius: 1000,
});
geoQuery.on("key_entered", function(key, location, distance) {
// dont forget that as shifts are added that match the geo, this will automatically add to the shiftKeys array
//shiftKeys = [];
shiftKeys.push(key)
console.log("Found shift " + key + " at " + location + " (" + distance + " km away)");
});
geoQuery.on("ready", () => {
shifts = []; // we need to blow out the array every time this function runs or it will throw errors
shiftKeys.forEach((shiftKey) => {
//console.log(shiftKey);
let shiftsRef = firebaseApp.database().ref('shifts').child(shiftKey);
shiftsRef.on("value", (snapshot) => {
//console.log(snapshot.val())
//if (snapshot.val().state == "WA" && (snapshot.val().licenseRequired == "CNA" || snapshot.val().licenseRequired == "RN")) {
//if (snapshot.val().licenseType == this.state.licenseType || snapshot.val().licenseRequired == "TEST") {
shifts.push({
key: snapshot.key,
fname: snapshot.val().fname,
lname: snapshot.val().lname,
company: snapshot.val().company,
address1: snapshot.val().address1,
address2: snapshot.val().address2,
city: snapshot.val().city,
state: snapshot.val().state,
zip: snapshot.val().zip,
shiftDate: snapshot.val().shiftDate,
shiftStart: snapshot.val().shiftStart,
shiftLength: snapshot.val().shiftLength,
shiftDescription: snapshot.val().shiftDescription,
licenseType: snapshot.val().licenseType,
logo: snapshot.val().logo,
building: snapshot.val().building,
}) // end shifts.push
var date_sort_asc = function (date1, date2) {
if (date1.shiftDate > date2.shiftDate) return 1;
if (date1.shiftDate < date2.shiftDate) return -1;
return 0;
};
//}
//console.log(this.state.distancePref)
this.setState({
dataSource: shifts,
resultCount: shifts.length,
})
}); // end shiftsRef.on
}); // end shiftKeys map
}); // end geoQuery.on
console.log('ShiftArray: ', shifts)
console.log('StateArray: ', this.state.dataSource)
}
render() {
const listItems = this.state.dataSource.map((shift) =>
<li key={shift.key}>
{shift.address1}
</li>
);
console.log('ShiftArray: ', shifts)
console.log('StateArray: ', this.state.dataSource)
return (
<Flexbox flexDirection="column" minHeight="100vh">
<Flexbox element="header" height="60px">
Header link one
</Flexbox>
<Flexbox flexGrow={1}>
<Flexbox
width="20%"
minWidth="200px"
maxWidth="300px"
style={{ backgroundColor: '#ba0000' }}>
Sidebar Menu Goes Here
</Flexbox>
<Flexbox width="80%" flexDirection="row" style={{ backgroundColor: '#FFF' }}>
<div>List of Shifts Addresses</div>
<ul>{listItems}</ul>
</Flexbox>
</Flexbox>
<Flexbox element="footer" height="60px">
Footer
</Flexbox>
</Flexbox>
);
}
}
Now Im getting Uncaught TypeError: this.state.dataSource.map is not a function
The problem was that I failed to initialize dataSource in the state, and then after that, I initialized it with a string instead of an empty array.
Was missing: dataSource: [] in this.setstate in the constructor.