Basic firestore get request problem in simple Gatsby website - reactjs

I have created a simple get request, but I am unsure what to do as it has asked for a key. This is a reduced version of my code:
import React, { Component, useEffect } from 'react';
import firebase from "gatsby-plugin-firebase";
var db = firebase.firestore();
var word1 = db.collection("menue").doc("word1");
class newMenue extends Component {
constructor(props) {
super(props)
this.state = {
wordOne: "",
}
}
render() {
word1.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
} else {
console.log('Document data:', doc.data());
this.setState({
wordOne: doc.data()
})
}
})
.catch(err => {
console.log('Error getting document', err);
});
return (
<div>
<h2 >{this.state.wordOne}</h2>
<br/>
<br/>
<h5>4 Slices of grilled Cypriot Halloumi</h5>
</div>
)
}
}
export default newMenue
this is my database:
Document data: {One: "hi"}
This is what the dev tools states.
Error: Objects are not valid as a React child (found: object with keys
{One}). If you meant to render a collection of children, use an array
instead.
What do I do in order to add the key to display the information?
Specifically, I am trying to set up a website for my friend's burger place where he can put in the menu changes on the website himself and I am stuck at this last bit.

You are trying to render an object:
<h2>{this.state.wordOne}</h2> // which is {One: "hi"}
You need to render the property of the object:
<h2>{this.state.wordOne.One}</h2>

Related

How to filter data from an object array according to their property values, in ReactJs?

I wrote a simple code to just pass all the data taken from a firebase real time database into a react component, which is called cubetemplate, via an object array. Here is a screenshot of the firebase I used to do the test:
Here's the code I used:
<div className="gal">
{Object.keys(this.state.gallery)
.sort()
.map(item => {
//inserting into the component
return <Cubetemplate source={this.state.gallery[item]["img"]} key={item}/>;
})}
</div>
Now, the problem is that, I want to pass only the objects which verify the condition "type"="vid", into the Cubetemplate component. How do I accomplish this?
Here's the full code to take a better understanding of the situation:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import Cubetemplate from './components/cubes/Cubetemplate';
import './Gallery.css';
import * as firebase from 'firebase';
class Gallery extends Component {
//this is the place where you declar your vars
constructor(props) {
super(props);
this.state = {
loading: false,
list: [1, 2, 3],
value: '',
gallery:{},
isLoggedIn: false,
};
this.readDB = this.readDB.bind(this); //no idea what the hell this is
}
readDB() {
var that = this;
var starCountRef = firebase.database().ref("/gallery");
//the values from the firebase will be printed in the console
starCountRef.once('value', snapshot => {
snapshot.forEach(function (childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
console.log("Key: " + childKey + " Title: " + childData.title);
});
that.setState({ isLoggedIn: true });
//just to check the values of snapshot
// console.log(snapshot.val());
//takes the data from friebase to snapshot to news.
this.setState({ gallery: snapshot.val() });
console.log(this.gallery);
}, err=> {
//errors
console.log(err);
});
};
componentDidMount(){
this.readDB();
};
render() {
return (
<div className="gallerycontainer">
<div className="gal">
{Object.keys(this.state.gallery)
.sort()
//map
.map(item => {
//inserting into the component
return <Cubetemplate source={this.state.gallery[item]["img"]} key={item}/>;
})}
</div>
</div>
);
}
}
export default Gallery;
Thanks in advance stackoverflowers!
You could use firebase filtering like Frank mentioned or you could simply do some conditional rendering like this:
<div className="gal">
{Object.keys(this.state.gallery)
.sort()
.map(item => {
//Only render if the item type is 'vid'
if (this.state.gallery[item]["type"] == "vid") {
<Cubetemplate source={this.state.gallery[item]["img"]} key={item}/>;
}
})}
</div>
To load only the gallery child nodes with type equal to vid, you'd use a Firebase query like this:
var galleryRef = firebase.database().ref("/gallery");
var query = galleryRef.orderByChild("type").equalTo("vid");
query.once('value', snapshot => {
...
For more on this, I highly recommend studying the Firebase documentation on ordering and filtering data.

ReactJS:setState could not work in Syncfusion React Toast

I using Syncfusion React Toast with singnalr for showing popup notification to user. but after fetch data from server with signalr and set message state with fetched value, toast content shows initial value which it`s assigned in constructor function.
import React, { Component } from 'react';
import { ToastComponent } from '#syncfusion/ej2-react-notifications';
import * as signalR from '#aspnet/signalr';
class Notification extends ToastComponent {
constructor() {
super(...arguments);
this.position = { X: 'Right', Y: 'Top' }
this.state = { message: '555'}
}
toastCreated() {
this.toastInstance.show({ timeOut: 0 })
}
componentDidMount() {
const notifConn = new signalR.HubConnectionBuilder().withUrl("/notif").build();
notifConn.on("ReceiveMessage",(msg) => {
this.setState({ message: msg });
});
notifConn.start().then(function () {
notifConn.invoke("SendNotification");
}).catch(function (er) {
console.log(er.toString());
});
}
render() {
return (
<div>
<ToastComponent
id='toast_target'
ref={toast => this.toastInstance = toast}
title={this.state.message}//it shows 555!!!
content={this.state.message} //it shows 555!!!
position={this.position}
showCloseButton
created={this.toastCreated = this.toastCreated.bind(this)}
/>
</div>
);
}
}
export default Notification;
Greetings from Syncfusion support.
By default, we couldn’t modify the content dynamically for existing displaying toasts. Dynamically changed content will be shown on notify the next toasts.
So, we suggest you to hide the previous toast and show the next toast after setState.
Please find the below code for your reference.
Chat.js
constructor(props) {
super(props);
this.state = {
message: '',
};
componentDidMount = () => {
const nick = window.prompt('Your name:', 'John');
const hubConnection = new HubConnection('http://localhost:5000/chat');
this.setState({ hubConnection, nick }, () => {
…………..
this.state.hubConnection.on('sendToAll', (nick, receivedMessage) => {
this.setState({ message: receivedMessage }); // Change the toast content using state
this.toastObj.hide(); // You can hide the toast
this.toastObj.show(); // You can show the toast
});
});
};
create = () => {
this.toastObj.show({ timeOut: 0 });
}
<ToastComponent ref={ (toast) => { this.toastObj = toast; } } content = { this.state.message } id = 'toast_default' created = { this.create.bind(this) } > </ToastComponent>
We have created sample for an ASP.NET core signalr with the react toast component.
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/AspNetCoreSignalRToastReact1983063835
Please find the below steps to run the above sample.
Navigate inside of AspNetCoreSignalR_React.Client folder and enter
‘npm install’.
Start the client app by entering ‘npm start’.
Run the AspNetCoreSignalR_React.Server project.
If the above solution doesn’t meet your requirement, kindly send the below details.
Have any reason on using static toasts with dynamic content update?
Regards,
Narayanasamy P.

When mapStateToProps in create create a new axios call

I have created a Reactjs component that receives a mapStateToProps function call. Everything works fine except the ajax call using Axios.
The class on a mapStateToProps update needs to call the server and add its payload to the state of the component and update the textarea.
The error I am getting from the console is,
ReactDOMIDOperations.js:47 Uncaught RangeError: Maximum call stack size exceeded
Below is what I have so far. Can anyone show me how to fix this issue?
import React from "react";
import { connect } from "react-redux";
import ApiCalls from "../../../utils/ApiCalls";
const mapStateToProps = state => {
return { passFilePath: state.passFilePath };
};
/**
* This component is a template to display
* widgets of information
*/
class IdeTextEditorClass extends React.Component {
constructor() {
super();
this.state = {
newData: [],
pathData: []
}
}
/**
* Received request from server add it to
* react component so that it can be rendered
*/
componentDidUpdate() {
try {
this.setState({ pathData: this.props.passFilePath[this.props.passFilePath.length - 1] });
} catch (err) {
this.setState({ pathData: '' });
}
console.log('path', this.state.pathData.data);
ApiCalls.readSassFile(this.state.pathData.data)
.then(function (serverData) {
this.setState({ newData: serverData[0].data })
}.bind(this));
}
render() {
try {
this.state.newData
} catch (err) {
this.setState({ newData: '' });
}
return (
<fieldset>
<input type="text" value={this.state.pathData.data} />
<textarea id="ide-text-area" name="ide-text-area" value={this.state.newData} /></fieldset>
)
}
}
const IdeTextEditor = connect(mapStateToProps)(IdeTextEditorClass);
export default IdeTextEditor;
class IdeTextEditorClass extends React.Component {
constructor() {
super();
/*
based on your original code it seems the default data should be empty string ,as you set them to be empty string when you cannot get data from server.
*/
this.state = {
newData: '',
pathData: ''
}
}
/**
* Received request from server add it to
* react component so that it can be rendered
*/
componentDidMount() {
try {
this.setState({ pathData: this.props.passFilePath[this.props.passFilePath.length - 1] });
} catch (err) {
this.setState({ pathData: '' });
}
console.log('path', this.state.pathData.data);
ApiCalls.readSassFile(this.state.pathData.data)
.then(function (serverData) {
this.setState({ newData: serverData[0].data })
}.bind(this));
}
render() {
//by default your newData is already empty string. so skip the checking here.
let path = this.state.pathData ? this.state.pathData.data : '';
return (
<fieldset>
<input type="text" value={path} />
<textarea id="ide-text-area" name="ide-text-area" value={this.state.newData} /></fieldset>
)
}
}
Explanation:
The major change is to change componentDidUpdate to componentDidMount.
Putting the data initializing logic in componentDidMount because:
called only once, thus avoiding the endless update loop mentioned in the comments. Also, initialization logic is usually expected here.
this method is called after initial render, so you can at least display something to user during the wait for data (from server). for example, in your render method, you can check newData and if it is not available, display a loading icon. Then React calls componentDidMount, and fetch your data -> update state -> trigger render again -> displays your input / text area using new data fetched from server. Of course, if you don't want to bother showing a loading icon, it is also fine, because your view will probably be updated quickly, when the ajax call returns.

getting Objects are not valid as a React child error

I'm trying to make a fetch request to my server and set the response json as the component's state.
I get this error
Objects are not valid as a React child (found: object with keys {description,
id, matched_substrings, place_id, reference, structured_formatting, terms, types}). If you meant to render a collection of children, use an array instead.
in li (at Search.js:25)
in ul (at Search.js:30)
in div (at Search.js:28)
in Search (at index.js:8)
this is my code -
import React from 'react'
import { ReactDOM } from "react-dom";
export default class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
places: []
}
this.handleUserInput = this.handleUserInput.bind(this)
}
handleUserInput(e) {
var searchInput = e.target.value;
var url = '/searchLocation/autocomplete?text=' + (searchInput)
if (searchInput.length > 2) {
fetch(url).then(function(response) {return response.json()})
.then((responseJson) => this.setState({places: responseJson["predictions"]}));
//.then( responseJson => console.log(responseJson["predictions"]))
}
}
render() {
const placeList = this.state.places.map(place =>
{
return <li>{place}</li>
});
return (
<div>
<input onChange={this.handleUserInput} type="text" id="PlaceSearch" />
<ul>
{ placeList }
</ul>
</div>
);
}
}
I found similiar errors by googling it but none helped..
Thank you very much for helping
This issue is you are trying to render place which I assume is an object inside the li.
you need to choose which property to render
ex:
return <li>{place.id}</li>
I think you're missing parenthesis;
const placeList = this.state.places.map(place =>
{
return ( <li>{place}</li> )
});

What is best approach to set data to component from API in React JS

We have product detail page which contains multiple component in single page.
Product Component looks like:
class Product extends Component {
render() {
return (
<div>
<Searchbar/>
<Gallery/>
<Video/>
<Details/>
<Contact/>
<SimilarProd/>
<OtherProd/>
</div>
);
}
}
Here we have 3 APIs for
- Details
- Similar Product
- Other Products
Now from Detail API we need to set data to these components
<Gallery/>
<Video/>
<Details/>
<Contact/>
In which component we need to make a call to API and how to set data to other components. Lets say we need to assign a,b,c,d value to each component
componentWillMount(props) {
fetch('/deatail.json').then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
OR
Do we need to create separate api for each components?
Since it's three different components you need to make the call in the component where all the components meet. And pass down the state from the parent component to child components. If your app is dynamic then you should use "Redux" or "MobX" for state management. I personally advise you to use Redux
class ParentComponent extends React.PureComponent {
constructor (props) {
super(props);
this.state = {
gallery: '',
similarPdts: '',
otherPdts: ''
}
}
componentWillMount () {
//make api call and set data
}
render () {
//render your all components
}
}
The Product component is the best place to place your API call because it's the common ancestor for all the components that need that data.
I'd recommend that you move the actual call out of the component, and into a common place with all API calls.
Anyways, something like this is what you're looking for:
import React from "react";
import { render } from "react-dom";
import {
SearchBar,
Gallery,
Video,
Details,
Contact,
SimilarProd,
OtherProd
} from "./components/components";
class Product extends React.Component {
constructor(props) {
super(props);
// Set default values for state
this.state = {
data: {
a: 1,
b: 2,
c: 3,
d: 4
},
error: null,
isLoading: true
};
}
componentWillMount() {
this.loadData();
}
loadData() {
fetch('/detail.json')
.then(response => {
// if (response.ok) {
// return response.json();
// } else {
// throw new Error('Something went wrong ...');
// }
return Promise.resolve({
a: 5,
b: 6,
c: 7,
d: 8
});
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
if (this.state.error) return <h1>Error</h1>;
if (this.state.isLoading) return <h1>Loading</h1>;
const data = this.state.data;
return (
<div>
<SearchBar/>
<Gallery a={data.a} b={data.b} c={data.c} d={data.d} />
<Video a={data.a} b={data.b} c={data.c} d={data.d} />
<Details a={data.a} b={data.b} c={data.c} d={data.d} />
<Contact a={data.a} b={data.b} c={data.c} d={data.d} />
<SimilarProd/>
<OtherProd/>
</div>
);
}
}
render(<Product />, document.getElementById("root"));
Working example here:
https://codesandbox.io/s/ymj07k6jrv
You API calls will be in the product component. Catering your need to best practices, I want to make sure that you are using an implementation of FLUX architecture for data flow. If not do visit phrontend
You should send you API calls in componentWillMount() having your state a loading indicator that will render a loader till the data is not fetched.
Each of your Components should be watching the state for their respective data. Let say you have a state like {loading:true, galleryData:{}, details:{}, simProducts:{}, otherProducts:{}}. In render the similar products component should render if it finds the respective data in state. What you have to do is to just update the state whenever you receive the data.
Here is the working code snippet:
ProductComponent:
import React from 'react';
import SampleStore from '/storepath/SampleStore';
export default class ParentComponent extends React.Component {
constructor (props) {
super(props);
this.state = {
loading:true,
}
}
componentWillMount () {
//Bind Store or network callback function
this.handleResponse = this.handleResponse
//API call here.
}
handleResponse(response){
// check Response Validity and update state
// if you have multiple APIs so you can have a API request identifier that will tell you which data to expect.
if(response.err){
//retry or show error message
}else{
this.state.loading = false;
//set data here in state either for similar products or other products and just call setState(this.state)
this.state.similarProducts = response.data.simProds;
this.setState(this.state);
}
}
render () {
return(
<div>
{this.state.loading} ? <LoaderComponent/> :
<div>
<Searchbar/>
<Gallery/>
<Video/>
<Details/>
<Contact/>
{this.state.similarProducts && <SimilarProd data={this.state.similarProducts}/>}
{this.state.otherProducts && <OtherProd data={this.state.otherProducts}/>}
</div>
</div>
);
}
}
Just keep on setting the data in the state as soon as you are receiving it and render you components should be state aware.
In which component we need to make a call to API and how to set data
to other components.
The API call should be made in the Product component as explained in the other answers.Now for setting up data considering you need to make 3 API calls(Details, Similar Product, Other Products) what you can do is execute the below logic in componentDidMount() :
var apiRequest1 = fetch('/detail.json').then((response) => {
this.setState({detailData: response.json()})
return response.json();
});
var apiRequest2 = fetch('/similarProduct.json').then((response) => { //The endpoint I am just faking it
this.setState({similarProductData: response.json()})
return response.json();
});
var apiRequest3 = fetch('/otherProduct.json').then((response) => { //Same here
this.setState({otherProductData: response.json()})
return response.json();
});
Promise.all([apiRequest1,apiRequest2, apiRequest3]).then((data) => {
console.log(data) //It will be an array of response
//You can set the state here too.
});
Another shorter way will be:
const urls = ['details.json', 'similarProducts.json', 'otherProducts.json'];
// separate function to make code more clear
const grabContent = url => fetch(url).then(res => res.json())
Promise.all(urls.map(grabContent)).then((response) => {
this.setState({detailData: response[0]})
this.setState({similarProductData: response[1]})
this.setState({otherProductData: response[2]})
});
And then in your Product render() funtion you can pass the API data as
class Product extends Component {
render() {
return (
<div>
<Searchbar/>
<Gallery/>
<Video/>
<Details details={this.state.detailData}/>
<Contact/>
<SimilarProd similar={this.state.similarProductData}/>
<OtherProd other={this.state.otherProductData}/>
</div>
);
}
}
And in the respective component you can access the data as :
this.props.details //Considering in details component.

Resources