How to insert value from state into a function? - reactjs

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.

Related

Covert object to array in react for plotly

How do you convert an object to an array in js ? (or is there a better way to dynamically update a nested object)
Basically I'm trying to update some data for the plotly component from an axios request on the fly, but plotly complains it is fed an object instead of an array.
Part of the code :
var trace0 = {
type: 'ohlc',
xaxis: 'x',
yaxis: 'y',
increasing: {line: {color: 'green'}},
decreasing: {line: {color: 'red'}},
line: {color: 'rgba(31,119,180,1)'},
x: ['2017-01-17'],
open: [118.339996],
high: [120.239998],
low: [118.220001],
close: [120],
};
var init_data = [trace0];
export default class GraphRAW extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
layout: {},
config: {},
checking:false,
revision: 0
};
}
getStart = () => {
const symbol = 'tst'
return axios
.post(API_URL + "startRAW", {symbol } )
.then((response) => {
if (response.data) {
var newData = Object.assign({}, this.state.data);
newData[0].x = response.data.x;
newData[0].close = response.data.close;
newData[0].open = response.data.open;
newData[0].low = response.data.low;
newData[0].high = response.data.high;
console.log('new data here =>');
console.log(newData); // Data seems ok in console
console.log(typeof(newData)); // Typeof new data is object
this.setState({data: newData });
this.setState({revision: this.state.revision++}); // Update revision to initiate a new plot in the plotly component
console.log('this data here =>');
console.log(this.data); // Undefined in console
console.log(typeof(this.data)); // Undefined in console
return response;
}
});
componentDidMount(){
this.setState({config: init_config});
this.setState({layout: init_layout});
this.setState({data: init_data});
this.getStart();
}
Warning: Failed prop type: Invalid prop data of type object supplied to PlotlyComponent, expected an array.
It seems response.data.x (~.close ~.open ~.low ~.high) are objects which need to be converted to an array in the data object.
In the render :
<Plot useResizeHandler data={this.state.data} layout={this.state.layout} config={this.state.config} revision={this.state.revision} style={{width: "100%" }} />
Any help highly appreciated
Posting my comment as an answer:
You just need to wrap the values returned from the server in an array, e.g:
newData[0].x = [response.data.x];

FlatList in react native not rendering data from state

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
}

React app won't render info from props even though I can console log it

So ideally my parent component is mapping through a database and rendering them based on the user's choice. Right now right now the information is being passed correctly and the app is rendering what I need it too (the card component) in the correct amount however it is full of dummy info. (Someone clicks beer, there are three beer types in the database, the app renders three card components full of dummy info).
Here is the parent component:
class Render extends React.Component {
constructor(props) {
super(props);
console.log("Here are your props", props);
}
componentDidMount() {
let options = {
method: 'GET',
url: 'http://localhost:8080/drinks',
};
let drinks = [];
console.log("this is",this);
axios.request(options)
.then( (response) => {
console.log(response);
this.setState({ drinks: response.data })
})
.catch(function (error) {
console.log(error);
});
}
render() {
console.log("this.state is",[this.state])
let stateArray = [this.state]
if (stateArray[0] == null) {
console.log("returning with nothing")
return <div></div>
}
let firstElement = stateArray[0];
let drinks = firstElement.drinks;
let drinkChoice = this.props.reduxState.drinkChoice
console.log("drinkchoice is here" , drinkChoice)
// const props = this.props
console.log("just drinks", drinks)
let drinkInfo = {
type: this.state.drinks.type,
name: this.state.drinks.name,
manufacturer: this.state.drinks.manufacturer,
rating: this.state.drinks.rating,
date: this.state.drinks.date,
description: this.state.drinks.description,
favorite: this.state.drinks.favorite
}
let cardComponents = drinks.map((drink) =>{
if (drink.type === drinkChoice) {
return (<InfoCard props={this.state.drinks} />)
} else {
return <div>Nothing to Report</div>
}})
return (
<div>
<div>{cardComponents}</div>
</div>
)
}
}
export default Render
Now I need it to render the actual database information for each entry. In the child/cardcomponent- I can console.log the props and it will correctly show the right information. It's getting through. But anytime I try to be more specific ( props.name ) it turns to undefined.
I have been at this for days and i'm so confused. The information is right there! I just need to grab it!
Here is the code for the child/card component:
const useStyles = makeStyles(theme => ({
root: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
expand: {
transform: 'rotate(0deg)',
marginLeft: 'auto',
transition: theme.transitions.create('transform', {
duration: theme.transitions.duration.shortest,
}),
},
expandOpen: {
transform: 'rotate(180deg)',
},
avatar: {
backgroundColor: red[500],
},
}));
export default function InfoCard(props) {
const classes = useStyles();
if( props.length <= 0 ) {
return (<div></div>);
} else {
console.log("props are here")
console.log( props )
console.log("props dot name")
console.log ( props.name )
}
props.each(function (drink) {
console.log(drink.name);
});
return (
<Card className={classes.root}>
title = { props.name }
</Card>
);
}
Where have I gone wrong? I feel like i've tried every possible iteration of console.log and drink.name. I'm at the end of my rope.
Thanks for any and all guidance.
sccreengrab of console log
What you're seeing in your log of props is an array of objects. Those objects have names, but the array itself does not, so when you console.log(props.name) it doesn't work and you see undefined. If you try console.log(props[0].name), for instance, you should see a name.
But what's strange here is that props should NOT be an array: it should be an object (whose keys map to the JSX element's attributes). For instance:
<InfoCard name="Bob"/>
would create a props object of:
{name: 'Bob'}
But when you log your props, you see an array, and that means you've somehow/somewhere replaced the actual props object with an array. Without seeing the code where you actually create <Infocard>, I can't speak to the details.
P.S. It might be possible to do this if you did something like:
`<MyComponent {...[{ name: 'Bob']} />`
... but honestly I'm not sure if that even works, and it seems like a bad idea even if it does.

Modify a property of an element that is inside of react state

I have this program that brings an article from my data base in componentDidMount(), fragmentedArticle() grabs each word and put it in this.state.fragmented and each word is put it in a span tag in this.state.fragmentedTags
I print the article in grey color text, but I want to change the style color property of the text (with a setTimeout every 1000 milliseconds) but I don't know if it's posible to changed a property of a tag that is save it in the react state.
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
export default class ArticleDetails extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
title: '',
article: '',
date: new Date(),
lenguages: [],
articles: [],
fragmented: [],
fragmentedTags: [],
showSpans: false,
counterSpaces: 0,
}
this.deleteArticle = this.deleteArticle.bind(this);
this.fragmentedArticle = this.fragmentedArticle.bind(this);
this.coloredArticle = this.coloredArticle.bind(this);
}
componentDidMount() {
this.setState({
id: this.props.match.params.id
})
// get individual exercise.
axios.get('http://localhost:5000/articles/'+ this.props.match.params.id)
.then(response => {
this.setState({
title: response.data.title,
article: response.data.article,
duration: response.data.duration,
date: new Date(response.data.date)
})
})
.catch(function (error) {
console.log(error);
})
// get all lenguages.
axios.get('http://localhost:5000/lenguages/')
.then(response => {
if (response.data.length > 0) {
this.setState({
lenguages: response.data.map(lenguage => lenguage.lenguage),
})
}
}).catch( error => console.log(error) )
}
deleteArticle( id ) {
axios.delete( 'http://localhost:5000/articles/' + id )
.then( res => console.log( res.data ) );
this.setState({
articles: this.state.articles.filter( el => el._id !== id )
}
)
}
fragmentedArticle = () => {
let length = this.state.article.length;
let word = [];
let fragmentedArticle = [];
let counter = 0;
let p1, p2 = 0;
for (let x = 0; x <= length; x++) {
word[x] = this.state.article[x];
if( this.state.article[x] === ' ' || this.state.article[x] === "\n" ){
p2 = x;
fragmentedArticle[counter] = word.join('').substr(p1,p2);
p1 = p2
p2 = 0;
counter++;
}
}
// we save each word
this.setState({
fragmented: fragmentedArticle,
counterSpaces: counter,
showSpans: !this.state.showSpans,
})
// we save each word wrapped in a span tag with a property of color grey.
this.setState( prevState => ({
fragmentedTags: prevState.fragmented.map( (name, index) =>
<span key={ index } style={{color:'grey'}} >{name}</span>
)
}))
}
coloredArticle = () => {
console.log(this.state.fragmentedTags[0].props.style.color);
// I see the actual value color style property of the span tag (grey) but I want to change it on green from the this.state.fragmentedTags[0] to the last word within a x period of time with the setTimeout js method.
// this code bellow change the color but not one by one.
this.setState( prevState => ({
fragmentedTags:
// map all the elements
prevState.fragmented.map( (name, index) =>
// with a delay of 1500 milliseconds
setTimeout(() => {
<span key={ index } style={{color:'green'}} >{name}</span>
}, 1500)
)
})
)
}
render(props) {
const displaySpan = this.state.showSpans ? 'inline-block' : 'none';
const {fragmentedTags} = this.state
return (
<div>
<h6>{ this.state.title }</h6>
{/* this show/hide the article text */}
<p onClick={ this.fragmentedArticle }>Show</p>
{/* I want to changed the text color one by one within a period of time (velocity, setTimeout) */}
<p onClick={ this.coloredArticle }>Play</p>
{/* Show us the full article (each word wrapped in a span with its property) */}
<div style={{ display:displaySpan }}>
{ fragmentedTags }
</div>
</div>
)
}
}
You shouldn't be transforming state like that. It gets very difficult to debug your application and makes it much more difficult to do simple things.
Download your articles and save them into state but if you need to make any other changes save it into a new part of state rather than overwriting current state. Most likely you do not need to save transformations into state though.
To answer your question, I would set a timestamp for each article and once its downloaded set a timer that will rerender the article with the new changes if sufficient time has passed.

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

Resources