FlatList in react native not rendering data from state - reactjs

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
}

Related

how to update/re-render a React list after an item is deleted, using classes

Thanks for any support. I'm learning React and need to solve the problem consisting in that I can't make React to re-render after an item is deleted from a list.
Firstly I would like to say that I have follow the answers I found searching but still no luck.
The scenario is that I'm using React to fetch a list from and API and render it in the same screen with a form for editing and listing the specific information for every item in the list (fields are just name and lastname). The list is displayed with a button for edit which makes the form for edit, and with another button for delete. The list displays the two only fields which are name and lastname which are displayed using ListGroupItem from reacstrap that when onClick uses the form for listing only. I also have the logic for add items.
I'm able to add, update, list with no problems and re-rendering properly. However when deleting I'm just able to delete the item from the API but I have to manually re-render to display the update list
import React, { Component } from "react";
import { Button, Container, Row, Col } from "reactstrap";
import ListBebes from "./components/ListBebes";
import AddBebeForm from "./components/AddBebeForm";
import EditBebeForm from "./components/EditBebeForm";
import { fetchBebes, fetchBebe, addBebe, deleteBebe } from "./api";
import Websocket from "react-websocket";
class App extends Component {
constructor(props) {
super(props);
this.state = {
bebes: [],
bebe: {},
current_bebe_id: 0,
is_creating: true,
is_fetching: true,
is_justRead: true,
has_updated: false,
};
this.socket = React.createRef();
this.focusSocket = this.focusSocket.bind(this);
this.handleItemClick = this.handleItemClick.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
this.handleAddBebe = this.handleAddBebe.bind(this);
this.getData = this.getData.bind(this);
this.handleSaveBebe = this.handleSaveBebe.bind(this);
this.handleOnNombresChange = this.handleOnNombresChange.bind(this);
this.handleOnApellidosChange = this.handleOnApellidosChange.bind(this);
}
componentDidMount() {
this.getData();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.has_updated === true) {
this.getData();
this.setState({ has_updated: false });
}
}
focusSocket() {
this.socket.current.focus();
}
async getData() {
let data = await fetchBebes();
this.setState({ bebes: data, is_fetching: false });
}
async handleItemClick(id) {
let selected_bebe = await fetchBebe(id);
this.setState((prevState) => {
return {
is_creating: false,
is_justRead: true,
current_bebe_id: id,
bebe: selected_bebe,
};
});
}
async handleEditClick(id) {
let selected_bebe = await fetchBebe(id);
this.setState((prevState) => {
return {
is_creating: false,
is_justRead: false,
current_bebe_id: id,
bebe: selected_bebe,
};
});
}
async handleDeleteClick(id) {
let antesBebes = [...this.state.bebes];
console.log(antesBebes);
let index = antesBebes.findIndex((i) => i.id === id);
console.log(`the index es ${index} y el id es ${id}`);
await deleteBebe(id);
antesBebes.splice(index, 1);
console.log(antesBebes);
this.setState({ bebes: [...antesBebes], has_updated: true });
//this.setState({ bebes: this.state.bebes, has_updated: true });
//console.log(antesBebes);
console.log("it was deleted...");
//window.location.reload();
//this.setState((prevState) => {
//return {
//bebes: antesBebes,
//has_updated: true,
//};
//});
//this.getData();
}
handleAddBebe() {
this.setState((prevState) => {
return { is_creating: true };
});
}
async handleSaveBebe(data) {
await addBebe(data);
await this.getData();
}
handleData(data) {
let result = JSON.parse(data);
let current_bebe = this.state.bebe;
if (current_bebe.id === result.id) {
this.setState({ bebe: result });
}
}
handleOnNombresChange(e) {
let nombres = e.target.value;
let current_bebe = this.state.bebe;
current_bebe.nombres = nombres;
this.setState({
bebe: current_bebe,
has_updated: true,
});
const socket = this.socket.current;
socket.state.ws.send(JSON.stringify(current_bebe));
}
handleOnApellidosChange(e) {
let apellidos = e.target.value;
let current_bebe = this.state.bebe;
current_bebe.apellidos = apellidos;
this.setState({
bebe: current_bebe,
has_updated: true,
});
//const socket = this.refs.socket;
const socket = this.socket.current;
socket.state.ws.send(JSON.stringify(current_bebe));
}
render() {
return (
<>
<Container>
<Row>
<Col xs="10">
<h2>Hello</h2>
</Col>
<Col>
<Button color="primary" onClick={this.handleAddBebe}>
Create a new note
</Button>
</Col>
</Row>
<Row>
<Col xs="4">
{this.state.is_fetching ? (
"Loading..."
) : (
<ListBebes
bebes={this.state.bebes}
handleItemClick={(id) => this.handleItemClick(id)}
handleEditClick={(id) => this.handleEditClick(id)}
handleDeleteClick={(id) => this.handleDeleteClick(id)}
></ListBebes>
)}
</Col>
<Col xs="8">
{this.state.is_creating ? (
<AddBebeForm handleSave={this.handleSaveBebe} />
) : (
<EditBebeForm
handleNombresChange={this.handleOnNombresChange}
handleApellidosChange={this.handleOnApellidosChange}
bebe={this.state.bebe}
soloLeer={this.state.is_justRead}
/>
)}
<Websocket
ref={this.socket}
url="ws://127.0.0.1:8000/ws/bebes"
onMessage={this.handleData.bind(this)}
/>
</Col>
</Row>
</Container>
</>
);
}
}
export default App;
Can you debug the following lines? and print [...antesBebes] | this.state.bebes and antesBebes after line 3 ?
I have some suspension about these lines, Can't debug them though because you haven't added all your components in here.
1 antesBebes.splice(index, 1);
2 console.log(antesBebes);
3 this.setState({ bebes: [...antesBebes], has_updated: true });
My recommendation is to use one of the following to manage your state in react application:
React Hooks -- recommended for small application like yours link
React Redux -- link
I found the solution. It happened that by placing code in the delete function handleDeleteClick() and also in componentDidUpdate I was messing things up.
The final code for delete is:
async handleDeleteClick(id) {
let antesBebes = [...this.state.bebes];
let index = antesBebes.findIndex((i) => i.id === id);
await deleteBebe(id);
antesBebes.splice(index, 1);
await this.setState({ bebes: antesBebes });
}
This code may have other problems but as far as the original goal was, this solve the problem.

Data retrieved from state not display in FlatList

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
});

Unable to pass params successfully to another .js file/screen

I'm trying to pass params from one screen to another screen using react-navigation, the problem I'm encountering is that when I console.log the param itself, the console returns 'undefined'. I can't seem to pinpoint what I'm doing wrong exactly. Any help or guidance would be much appreciated.
I tried the following, with no success:
-this.props.navigation.getParam('biometryStatus')
-this.props.navigation.state.params('biometryStatus')
This is my AuthenticationEnroll screen where my param is being initialised as the state of the component:
export default class AuthenticationEnroll extends Component {
constructor() {
super()
this.state = {
biometryType: null
};
}
async _clickHandler() {
if (TouchID.isSupported()){
console.log('TouchID is supported');
return TouchID.authenticate()
.then(success => {
AlertIOS.alert('Authenticated Successfuly');
this.setState({biometryType: true })
this.props.navigation.navigate('OnboardingLast', {
pin: this.props.pin,
biometryStatus: this.state.biometryType,
});
})
.catch(error => {
console.log(error)
AlertIOS.alert(error.message);
});
} else {
this.setState({biometryType: false });
console.log('TouchID is not supported');
// AlertIOS.alert('TouchID is not supported in this device');
}
}
_navigateOnboardingLast() {
this.props.navigation.navigate('OnboardingLast', {pin: this.props.pin})
}
render () {
return (
<View style={{flex: 1}}>
<Slide
icon='fingerprint'
headline='Secure authentication'
subhead='To make sure you are the one using this app we use authentication using your fingerprints.'
buttonIcon='arrow-right'
buttonText='ENROLL'
buttonAction={() => this._clickHandler()}
linkText={'Skip for now.'}
linkAction={() => this._navigateOnboardingLast()}
slideMaxCount={4}
slideCount={2}
subWidth={{width: 220}}
/>
</View>
)
}
}
And this is my OnboardingLast Screen where my param is being passed down and printed through console.log:
class OnboardingLast extends Component {
async _createTokenAndGo () {
let apiClient = await this._createToken(this.props.pin)
this.props.setClient(apiClient)
AsyncStorage.setItem('openInApp', 'true')
const { navigation } = this.props;
const biometryStatus = navigation.getParam('biometryStatus', this.props.biometryStatus);
console.log(biometryStatus);
resetRouteTo(this.props.navigation, 'Home')
}
/**
* Gets a new token from the server and saves it locally
*/
async _createToken (pin) {
const tempApi = new ApiClient()
let token = await tempApi.createToken(pin)
console.log('saving token: ' + token)
AsyncStorage.setItem('apiToken', token)
return new ApiClient(token, this.props.navigation)
}
render () {
return (
<View style={{flex: 1}}>
<Slide
icon='checkbox-marked-circle-outline'
headline={'You\'re all set up!'}
subhead='Feel free to start using MyUros.'
buttonIcon='arrow-right'
buttonText='BEGIN'
buttonAction={() => this._createTokenAndGo()}
slideMaxCount={4}
slideCount={3}
/>
</View>
)
}
}
Expected Result is that console.log(biometryStatus); returns 'true' or 'false', however it returns 'undefined'.
Since setState is asynchron, you send null (declared in your constructor) to your next page. By doing so, you will send true:
this.setState({ biometryType: true })
this.props.navigation.navigate('OnboardingLast', {
pin: this.props.pin,
biometryStatus: true,
});
You could also do this, since setState can take a callback as param:
this.setState({ biometryType: true }, () => {
this.props.navigation.navigate('OnboardingLast', {
pin: this.props.pin,
biometryStatus: true,
});
})
In your second page this.props.biometryStatus is undefined.
The second argument of getParam is the default value. You should change it like that
const biometryStatus = navigation.getParam('biometryStatus', false);

React native data not rendered after setstate

So i have been working with firebase as a backend in my react native application, i have tried to fetch data this way but i have nothing rendered, i have the activity indicator that went off, but i get that the data array is empty in the application screen, and when i do a console.log, i can see the data in the console, but nothing shows off in the application screen, please help me it's been days that i'm struggling.
export default class Leaderboard extends React.Component{
constructor(props){
super(props)
this.state = {
loading : true,
data : []
}
}
componentDidMount(){
firebase.firestore().collection('rankings').get()
.then(res => {
let rankArray = []
res.forEach(document => {
rankArray.push(document.data())
})
return rankArray;
}).then(res =>{
let data = []
res.forEach(item =>{
firebase.firestore().doc(item.idUser.path)
.get()
.then(doc =>{
let dataItem = {}
dataItem.id = doc.ref.path
dataItem.name = doc.data().fullname
dataItem.points = doc.data().points
dataItem.lc = 'Oran'
data.push(dataItem)
dataItem = {}
})
})
return data;
}).then(res =>this.setState({
loading : false,
data : res
}) ).catch(err => console.log(err))
}
render(){
if(this.state.loading){
return(
<View style = {styles.container}>
<ActivityIndicator size= 'large'></ActivityIndicator>
</View>
)
}else{
console.log(this.state.data)
return(
<View>
<Text>{this.state.data.length}</Text>
<FlatList
data={this.state.data}
renderItem={({item}) => <Text>{item.fullname}</Text>}
/>
</View>
)
}
}
}
The reason for this not working as expected is that you're trying to perform an asynchronous function call, per iteration of your res array inside of your forEach() callback:
// This is asynchronous
firebase.firestore().doc(item.idUser.path).get().then(doc =>{ ... })
Consider revising your code to use the Promise.all() method instead. This will ensure that each asynchronous for individual documents per-item in res array is completed, before setState() in the susequent .then() handler is invoked:
.then(res => {
let rankArray = []
res.forEach(document => {
rankArray.push(document.data())
})
return rankArray;
})
.then(res => {
// Use promise all to resolve each aync request, per item in the
// res array
return Promise.all(res.map(item => {
// Return promise from .get().then(..) for this item of res array.
return firebase.firestore()
.doc(item.idUser.path)
.get()
.then(doc => {
let dataItem = {}
dataItem.id = doc.ref.path
dataItem.name = doc.data().fullname
dataItem.points = doc.data().points
dataItem.lc = 'Oran'
// Return resolve dataItem to array that is relayed to next .then()
// handler (ie where you call this.setState())
return dataItem
})
}));
})
.then(res =>this.setState({
loading : false,
data : res
}))

Cannot see firebase data in application - react native

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.")
}
}
}

Resources