How to query in firebase database in react? - javascript

I have the following data structure in firebase as a realtime database:
{
"react" : {
"url_01" : "https://stackoverflow.com/",
"url_02" : "https://google.com/",
"url_03" : "https://www.youtube.com/"
}
}
I'm trying to query the database in React to display all URLs in the below component.
So far I got it to display the first URL in the database correctly but now trying to display them all in the div as <h1>.
class FirebaseDB extends React.Component {
constructor() {
super();
this.state = {
speed: [],
};
}
componentDidMount() {
const rootRef = firebase.database().ref().child('react');
const speedRef = rootRef.child('url_01');
speedRef.on('value', snap => {
this.setState({
speed: snap.val()
});
});
}
render() {
return (
<div>
<h1>URL: {this.state.speed}</h1>
</div>
);
}
}

componentDidMount() {
const rootRef = firebase.database().ref();
const speedRef = rootRef.child('react');
speedRef.once("value", snap => {
// Handle state
let speedsUrls = []
snap.forEach(child => {
speedsUrls.push(child.val())
});
this.setState({speed: speedsUrls})
});
}
render() {
const SpeedURLS = this.state.speed.map(url => <h1>URL: {url}</h1>);
return (
<div>
{SpeedURLS}
</div>
);
}

Another solution:
const object1 = {
"url_01" : "https://stackoverflow.com/",
"url_02" : "https://google.com/",
"url_03" : "https://www.youtube.com/"
};
let a = Object.values(object1);
a is now
["https://stackoverflow.com/","https://google.com/","https://www.youtube.com/"]

Related

React component don't update the state properly with fetched data

I have these two components: I fetch the data in the parent component and then pass it to the child component, which formats it into an object so that it can be used in the construction of a graph.
When the child component is mounted for the first time, the graph is not rendered.
If I try to change the child component code to update it (e.g. delete linkColor from ForceGraph2D’s props), the graph will be displayed correctly.
I want to find a way to see it right away, the first time the component is mounted.
Parent component:
import React, {Fragment, useEffect, useState} from "react";
import AddressList from "./AddressList";
import ClusterGraph from "./ClusterGraph";
import axios from "axios";
function ClusterPage(props) {
const [cluster, setCluster] = useState([]);
const [subCluster, setSubCluster] = useState([]);
useEffect(() => {
fetchData1();
fetchData2();
},[]);
const fetchData1 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get('http://localhost:5000/', {
params: {address: address}
});
const data = await response.data;
setCluster(data);
}
const fetchData2 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get('http://localhost:5000/sub/', {
params: {address: address}
});
const data = await response.data;
setSubCluster(data);
}
return (
<div key={props.match.params.id}>
<Fragment>
<AddressList data={cluster} />
<ClusterGraph data1={cluster} data2={subCluster} />
</Fragment>
</div>
);
}
export default ClusterPage;
Child component:
import '../styles/ClusterGraph.css';
import ForceGraph2D from 'react-force-graph-2d';
import React from "react";
class MyClusterGraph extends React.Component {
constructor(props) {
super(props);
this.state = {nodes:[],links:[]};
}
componentWillMount() {
this.loadData();
}
loadData = async () => {
let nodes = this.props.data1.map(row => {
let id = row.address_id;
let addressHash = row.address_hash;
let nodeColor;
if(row.miner_address)
nodeColor="blue";
else
nodeColor="purple";
return {id:id,addressHash:addressHash,nodeColor:nodeColor};
});
let links = this.props.data2.map(row => {
let source = row.address_id_1;
let target = row.address_id_2;
let linkColor;
switch (row.link_type) {
case 0:
linkColor="green";
break;
case 1:
linkColor="red";
break;
case 2:
linkColor="cyan";
break;
}
return {source:source,target:target,linkColor:linkColor};
});
this.setState({nodes:nodes,links:links});
}
render() {
return (
<div className="graph">
<ForceGraph2D
graphData={this.state}
backgroundColor="white"
height={400}
width={700}
nodeLabel="addressHash"
nodeColor="nodeColor"
linkColor="linkColor" />
</div>
);
}
}
function ClusterGraph({data1,data2}) {
return (
<div className="section2">
<MyClusterGraph data1={data1} data2={data2} />
</div>
);
}
export default ClusterGraph;
You could make sure you render your graphs after the data is fully fetched, and show a loader in the meantime, like so:
function ClusterPage(props) {
const [cluster, setCluster] = useState([]);
const [subCluster, setSubCluster] = useState([]);
useEffect(() => {
fetchData1();
fetchData2();
}, []);
const fetchData1 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get("http://localhost:5000/", {
params: { address: address },
});
const data = await response.data;
setCluster(data);
};
const fetchData2 = async () => {
const address = props.match.params.addressHash;
const response = await axios.get("http://localhost:5000/sub/", {
params: { address: address },
});
const data = await response.data;
setSubCluster(data);
};
if (cluster.length <= 0 || subCluster.length <= 0) {
return <p>Loading...</p>;
}
return (
<div key={props.match.params.id}>
<Fragment>
<AddressList data={cluster} />
<ClusterGraph data1={cluster} data2={subCluster} />
</Fragment>
</div>
);
}
This way, you would use the constructor to format your data, as componentWillMount() is deprecated and considered unsafe:
class MyClusterGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: this.props.data1.map((row) => {
let id = row.address_id;
let addressHash = row.address_hash;
let nodeColor;
if (row.miner_address) nodeColor = "blue";
else nodeColor = "purple";
return { id: id, addressHash: addressHash, nodeColor: nodeColor };
}),
links: this.props.data2.map((row) => {
let source = row.address_id_1;
let target = row.address_id_2;
let linkColor;
switch (row.link_type) {
case 0:
linkColor = "green";
break;
case 1:
linkColor = "red";
break;
case 2:
linkColor = "cyan";
break;
}
return { source: source, target: target, linkColor: linkColor };
}),
};
}
render() {
return (
<div className="graph">
<ForceGraph2D
graphData={this.state}
backgroundColor="white"
height={400}
width={700}
nodeLabel="addressHash"
nodeColor="nodeColor"
linkColor="linkColor"
/>
</div>
);
}
}
function ClusterGraph({ data1, data2 }) {
return (
<div className="section2">
<MyClusterGraph data1={data1} data2={data2} />
</div>
);
}
export default ClusterGraph;
Try making your parent component calls synchronous currently it is sync individually but it calls in async fashion change your code base accordingly

How to call function from function array (React js)

I Created array that contains functions and now I am trying to call a function from that array, I tried this code but it's not working and I have no idea how can I do it becuase I am pretty new at react js. Can someone help me with this?
here is my code
import React, { Component } from "react";
import "../App.css";
export default class Chart extends Component {
constructor(props) {
super(props);
this.state = {
stockChartXValues: "",
stockChartYValues: "",
type: props.data,
};
}
getBTC = () => {
// .....
};
getETH = () => {
// .....
};
getADA = () => {
// .....
};
componentDidMount() {
// here I am trying to run a function according to the "type" variable
var options = ["BTC", "ETH", "ADA"];
var functions = [this.getBTC, this.getETH, this.getADA];
var func = functions.indexOf(options.indexOf(this.state.type));
func.call();
}
render() {
return (
<div>
<h1>Hello world</h1>
</div>
);
}
}
you need to get function with the index you found;
var func = functions.indexOf(options.indexOf(this.state.type));// this returns index not the actual func
functions[func] && functions[func]()
My Approach would be like;
getBTC = () => {
// .....
};
getETH = () => {
// .....
};
getADA = () => {
// .....
};
getCoin = (type) => {
switch(type) {
case "BTC": this.getBTC()
return
case "ADA":...
...
...
}
componentDidMount() {
this.getCoin(this.state.type)
}

Got an error while setting the 'state' (REACT)

I was doing a small project just to learn more about React when I got this error:
" Line 19.5: 'state' is not defined no-undef ".
I don't know what is wrong...
I used the same structure in another project and I didn't get an error.
import React, { Component } from 'react'
import axios from 'axios'
import Main from '../template/Main'
const headerProps = {
icon: 'users',
title: 'Usuários',
subtitle: 'Cadastro de usuários: Incluir, Listar, Alterar e Excluir'
}
const baseUrl = 'http://localhost:3001/users'
const initalState = {
user: { name: '', email: '' },
list: []
}
export default class UserCrud extends Component {
state = { ...initalState }
clear() {
this.setState({ user: initalState.user })
}
save() {
const user = this.state.user
const method = user.id ? 'put' : 'post'
const url = user.id ? `${baseUrl}/${user.id}` : baseUrl
axios[method](url, user)
.then(resp => {
const list = this.getUpdatedList(resp.data)
this.setState({ user: initalState.user, list })
})
}
getUpdatedList(user) {
const list = this.state.list.filter(u => u.id !== user.id)
list.unshift(user)
return list
}
render() {
return (
<Main {...headerProps}>
Cadastro de Usuários
</Main>
)
}
}
Error message
Declare state inside constructor method using this.
export default class UserCrud extends Component {
constructor(props) {
super(props);
this.state = { ...initalState };
}
clear() {
this.setState({ user: initalState.user })
}
save() {
const user = this.state.user
const method = user.id ? 'put' : 'post'
const url = user.id ? `${baseUrl}/${user.id}` : baseUrl
axios[method](url, user)
.then(resp => {
const list = this.getUpdatedList(resp.data)
this.setState({ user: initalState.user, list })
})
}
getUpdatedList(user) {
const list = this.state.list.filter(u => u.id !== user.id)
list.unshift(user)
return list
}
render() {
return (
<Main {...headerProps}>
Cadastro de Usuários
</Main>
)
}
}

how to merge local setState list to Redux list into one list - redux react

Here i have a difficult situation. I have a locationData json in JobsPanel component which is saving location details based on one id(jobId). Now, in my component i have a part 'Configured Location' where i am calling the saved location data and make a setState list ('configuredList') from that json. Now, i have one more part in my application preliminary locations data using redux action calling other api and save into a list 'conLocations'.
Now, i am adding one location item 'conLocation' list (redux state) to 'configuredList'(setState) and updating the changes. It is working fine but last added item showing two times. After trial, i do understand that i have rendered two mapped list. How to merge that into one ? I have done so far this.
configLocation function where i am retrieving last saved location from locationData json.
/** Currently initialize and configure configuredList for retrieving existing job's location data */
configLocation(locationData) {
let configuredList = [];
if (locationData.locations.locationDetails != null && locationData.locations.locationDetails != undefined) {
locationData.locations.locationDetails.map(item => {
let listitem = { ...item };
configuredList.push(listitem);
});
}
this.setState({ configuredList });
}
getLocationData function where i am merging two list that retrieved list and conLocations list and i am calling this function to other component where save changes or update changes operation is happening. It is working fine.
getLocationData() {
let saveableLocationlist = [];
if (this.props.conLocations != null && this.state.configuredList != null) {
const { configuredList } = this.state;
const { conLocations } = this.props;
let totalList = configuredList.concat(conLocations);
saveableLocationlist = totalList;
}
const locationData = {
locationDetails: saveableLocationlist
}
return locationData;
}
here you can see i am updating the locationData json . By calling this function in jobspanel that updated locationData json is now available for my component in 'configLocation' function.
My component code:
export class NewLocationPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
configuredList: [],
chkitems: []
};
this.configLocation = this.configLocation.bind(this);
this.togglePanel = this.togglePanel.bind(this);
this.handleClick = this.handleClick.bind(this);
this.allLocations = this.allLocations.bind(this);
this.clearall = this.clearall.bind(this);
this.getLocationData = this.getLocationData.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.removeConfigLocation = this.removeConfigLocation.bind(this);
this.removeLocationAll = this.removeLocationAll.bind(this);
this.handleChecklocation = this.handleChecklocation.bind(this);
this.handleCheckedAdded = this.handleCheckedAdded.bind(this);
this.handleCheckedRemove = this.handleCheckedRemove.bind(this);
this.handleActionButton = this.handleActionButton.bind(this);
}
componentDidMount() {
this.props.loadData();
if (this.props.locationData != null && this.props.locationData != undefined) {
this.configLocation(this.props.locationData);
}
}
componentDidUpdate(prevProps, prevState) {
if ((prevProps.jobId != this.props.jobId || prevProps.locationData != this.props.locationData)) {
this.configLocation(this.props.locationData);
}
}
//other codes
/** Currently initialize and configure configuredList for retrieving existing job's location data */
configLocation(locationData) {
let configuredList = [];
if (locationData.locations.locationDetails != null && locationData.locations.locationDetails != undefined) {
locationData.locations.locationDetails.map(item => {
let listitem = { ...item };
configuredList.push(listitem);
});
}
this.setState({ configuredList });
}
/** updating locationData by saving changes - calling this function into jobsPanel */
getLocationData() {
let saveableLocationlist = [];
if (this.props.conLocations != null && this.state.configuredList != null) {
const { configuredList } = this.state;
const { conLocations } = this.props;
let totalList = configuredList.concat(conLocations);
saveableLocationlist = totalList;
}
const locationData = {
locationDetails: saveableLocationlist
}
return locationData;
}
//other codes
render() {
//const{configuredList} = this.state;
const _labels = store.getLabels();
let collapsedToggle = this.props.open ? 'collapsed' : ''
return (
{this.state.open ? (
<div className="panel-body">
<div className="row grid-divider">
<div className="col-sm-6">
<div className="col-padding">
<div className="pos-div"><h4>Configured Location</h4>
<div><table className="table configTableColor"><thead>{this.state.configuredList.map((locc, index) => <tr key={index}><th><input type="checkbox" onClick={() => this.handleCheckedRemove(locc.mruCode)} /><label></label></th><th className="configLocationInfo">{locc.mruCode} - {_labels[locc.division]} - {locc.country}</th><th className="text-right"><img alt="DeleteIcon" onClick={() => { this.removeConfigLocation(index) }} className="deleteIconStyle" src="img/delete_large_active.png" /></th></tr>)}</thead>
<tbody>
{this.props.conLocations.map((loct, index) => <tr key={index}>
<td><input type="checkbox" /><label></label></td>
<td className="configLocationInfo">{loct.mruCode} - {_labels[loct.division]} - {loct.country}</td>
<td className="text-right"><img alt="DeleteIcon" onClick={() => this.handleRemove(loct.mruCode)} className="deleteIconStyle" src="img/delete_large_active.png" /></td>
</tr>
)}
</tbody></table></div>
</div>
</div>
</div>
</div>) : null}
</div>
);
}
}
const mapStateToProps = state => {
return {
location: state.locationRed.location,
conLocations: state.locationRed.conLocations,
isChecked: state.locationRed.isChecked
};
};
const mapDispatchToProps = (dispatch) => {
return {
loadData: () => { dispatch(loadData()) },
addLocation: (mruCode) => { dispatch(addLocation(mruCode)) },
addAllLocation: () => { dispatch(addAllLocation()) },
removeLocation: (mruCode) => { dispatch(removeLocation(mruCode)) },
removeAllLocation: () => { dispatch(removeAllLocation()) },
checkboxState: (mruCode) => { dispatch(checkboxState(mruCode)) },
checkedLocation: () => { dispatch(checkedLocation()) }
}
}
export default connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(NewLocationPanel);
As you can see i am rendering two list. How to merged into one?
Jobs Panel component where i am initialize and saving locationData details
import React from 'react';
import ReactDOM from 'react-dom';
import LocationPanel from '../panels/NewLocationPanel';
class JobsPanelComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
jobDetailJson: this.props.jobDetailJson
};
this.setLocationPanelRef = cRef =>{this.locationPanel = cRef;};
}
componentWillUnmount() {
this.clearStates();
this.clearRefs();
this.clearBindings();
}
clearStates() {
this.state.jobDetailJson = null;
}
clearRefs(){
this.locationPanel = null;
}
clearBindings(){
this.setLocationPanelRef = null;
}
componentWillMount() {
this.state.jobDetailJson = this.props.jobDetailJson;
}
componentWillReceiveProps(nextProps) {
this.state.jobDetailJson = nextProps.jobDetailJson;
}
saveJobData(jobData){
var locationData = null;
if(some conditions){
locationData = this.locationPanel.getWrappedInstance().getLocationData();
}
//more other lines not related to my mine
}
render(){
var locationDataJson= null;
if(this.state.jobDetailJson != null){
locationDataJson =this.state.jobDetailJson;
}
return(<div className="panel-group" id="jobsPanelGroup">
<LocationPanel ref={this.setLocationPanelRef} locationData ={locationDataJson} jobDetailJson={this.state.jobDetailJson} versionId={versionId} jobName={jobName} jobId={jobId} isForViewOnly={this.props.isForViewOnly} parentJobId={this.props.parentJobId} title="Location"/>
//More coded lines for other things not related to my part
);
}
}
My application flow will be like - Configured Location(initial) configuredList -> conLocations (redux list) -> conLocations(add item) -> Configured Location(intermediate) configuredList + added item(conLocations) -> save changes -> Configured Location(final) - merged List
save changes /update locationData everything is in Jobs Panel but working fine. There is no problem. How to make changes in my component.
The mapStateToProps function is passed both the redux state and the component's props. So you can combine your locations from redux and from props inside mapStateToProps:
// destructuring only the things we need from state (locationRed) and props (locationData)
const mapStateToProps = ({ locationRed }, { locationData }) => ({
location: locationRed.location,
// get a merged set
conLocations: [...locationRed.conLocations, ...(locationData.locations.locationDetails || [])],
isChecked: locationRed.isChecked
})
With this setup you could most likely eliminate your configuredList state and related update functions, your componentDidUpdate function and just render from props.conLocations instead of from state and props in two separate loops.
You could also dedupe locations or do any job id checks you need inside of mapStateProps when merging your lists. If it starts to gets a bit complicated in your mapStateToProps, you could take a look at memoized selectors like reselect that would make that a lot nicer.

React state updating but not rendering on child component

I know the state is updating because 1. the 'Loading...' is going away, I can console log this.state.images to see the array. However when the state updates and the loading goes the searchbar shows up but the Card's within CardList do not.
They do show up when I search for a correct string, but not before.
If I pass this.state.images to CardList they show up perfectly. However when I move to the filteredImages they only show up when filtered.
Any ideas? Thanks in advance.
class App extends Component {
constructor() {
super();
this.state = {
images:[],
searchfield: ''
}
}
getLabels = (image) => {
const AuthKey = key.key;
const res = axios.post(`https://vision.googleapis.com/v1/images:annotate?key=${AuthKey}`, {
requests: [
{
image:{
source:{
imageUri: `http://storage.googleapis.com/${image}`
}
},
features:[
{
type:"LABEL_DETECTION",
maxResults:10
}
]
}
]
});
res.then(function (response) {
const results = response.data.responses[0].labelAnnotations;
const ex = results.map(result => {
return result.description;
});
return ex;
});
return res;
};
componentDidMount() {
imageFiles.imageFiles.forEach(img => {
this.getLabels(img).then(result => {
const results = result.data.responses[0].labelAnnotations;
const labels = results.map(result => {
return result.description;
});
//Add new values to the state
this.setState({images:[...this.state.images, {img, labels}]});
});
})
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
}
render() {
const filteredImages = this.state.images.filter(image => {
return image.labels.includes(this.state.searchfield.toLowerCase());
});
// Create an array of objects to store the image path and the labels detected from Google Vision
if (this.state.images.length === 0) {
return <h1>Loading...</h1>
} else {
return (
<Grid className="App">
<SearchBox searchChange={this.onSearchChange}/>
<CardList images={filteredImages} />
</Grid>
)}
}
}
export default App;
class App extends Component {
constructor() {
super();
this.state = {
images:[],
searchfield: '',
filteredImages:[]
}
}
getLabels = (image) => {
const AuthKey = key.key;
const res = axios.post(`https://vision.googleapis.com/v1/images:annotate?key=${AuthKey}`, {
requests: [
{
image:{
source:{
imageUri: `http://storage.googleapis.com/${image}`
}
},
features:[
{
type:"LABEL_DETECTION",
maxResults:10
}
]
}
]
});
res.then(function (response) {
const results = response.data.responses[0].labelAnnotations;
const ex = results.map(result => {
return result.description;
});
return ex;
});
return res;
};
componentDidMount() {
imageFiles.imageFiles.forEach(img => {
this.getLabels(img).then(result => {
const results = result.data.responses[0].labelAnnotations;
const labels = results.map(result => {
return result.description;
});
//Add new values to the state
this.setState({images:[...this.state.images, {img, labels}]});
this.setState({filteredImages:[...this.state.images, {img, labels}]});
});
})
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
let filteredImages = this.state.images.filter(image => {
return image.labels.includes(this.state.searchfield.toLowerCase());
});
this.setState({filteredImages});
}
render() {
// Create an array of objects to store the image path and the labels detected from Google Vision
if (this.state.images.length === 0) {
return <h1>Loading...</h1>
} else {
return (
<Grid className="App">
<SearchBox searchChange={this.onSearchChange}/>
<CardList images={this.state.filteredImages} />
</Grid>
)}
}
}
export default App;

Categories