Dynamic Buttons creation React - reactjs

I am trying to fetch the firebase data into a array and for each element create a button with that element as an id and name.
import React, { Component } from 'react'
import app from './firebase'
import firebase from "firebase/app";
import "firebase/database"
import { BsFillSquareFill } from "react-icons/bs";
import { Container,Row, Col } from "react-bootstrap";
import { withRouter } from 'react-router-dom';
var chambers = []
export default class ChamberClass extends Component {
constructor(props) {
super(props);
}
getButtonsUsingMap = () => {
return chambers.map((number) => {
return <button id={number} onClick={this.routeChange} className="btn"><BsFillSquareFill key = {number} color='green' className="icon "/>
<center>{number}</center>
</button>
})
}
componentDidMount(){
var chamberListen = firebase.database().ref()
chamberListen.on('value', snapshot => {
snapshot.forEach((cham) => {
var chamKey = cham.key;
var chamData = cham.val();
chambers.push(chamKey)
// document.getElementById("Chambers").innerHTML = chambers
console.log(chambers)
})
})
}
render() {
return (
<div>
<h4 className='RoomsTitle'>Rooms</h4>
<hr></hr>
{this.getButtonsUsingMap()}
</div>
)
}
}
I do get the console log which probably means that the firebase data is being accessed properly. However no buttons are being created.
Also when i move the code block inside componentDidMount() to the top of the ChamberClass, the buttons do get displayed but only once. After every successive attempt after reload or manually going to the route doesnt help either.

You need to put the array chambers into the component state. Without that you change the value but your component doesn't know that something changed and wont render as you expect it. By putting it into the component state it will know when it changes:
import React, { Component } from "react";
import app from "./firebase";
import firebase from "firebase/app";
import "firebase/database";
import { BsFillSquareFill } from "react-icons/bs";
import { Container, Row, Col } from "react-bootstrap";
import { withRouter } from "react-router-dom";
export default class ChamberClass extends Component {
state = {
chambers: [],
};
constructor(props) {
super(props);
}
getButtonsUsingMap = () => {
return this.state.chambers.map((number) => {
return (
<button id={number} onClick={this.routeChange} className="btn">
<BsFillSquareFill key={number} color="green" className="icon " />
<center>{number}</center>
</button>
);
});
};
componentDidMount() {
var chamberListen = firebase.database().ref();
chamberListen.on("value", (snapshot) => {
var chambers = [];
snapshot.forEach((cham) => {
var chamKey = cham.key;
var chamData = cham.val();
chambers.push(chamKey);
// document.getElementById("Chambers").innerHTML = chambers
console.log(chambers);
});
this.setState({ chambers });
});
}
render() {
return (
<div>
<h4 className="RoomsTitle">Rooms</h4>
<hr></hr>
{this.getButtonsUsingMap()}
</div>
);
}
}

Related

ReactJs - Not able to display all data at once. The loop to get the data runs automatically by re-rendering one by one and stops at the last data set

Image of my ReactJS web page and the console view to see the data structure
My code below:
import React, {
Component,
useState,
useEffect
} from 'react';
import './App.css';
import {
Card,
CardImg,
CardText,
CardBody,
CardTitle,
CardSubtitle,
Button
} from 'reactstrap';
import Image from 'react-image-resizer';
import * as firebase from 'firebase';
class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
async getOrderbookData() {
try{
var clientArray = ['Client1', 'Client2', 'Client3', 'Client4',
'Client5', 'Client6', 'Client7'];
for (var i=0; i<clientArray.length; i++) {
const db = firebase.firestore();
await db.collection('Orderbook').get().then((querySnapshot) => {
var orderbooks = []
var orderbook = []
querySnapshot.forEach(doc => {
orderbooks.push(doc.data())
console.log('THISS'+ orderbooks)
});
orderbook = orderbooks[i].body
console.log('i value:' + i);
this.setState({
orderbook: orderbook
})
});
}
}catch (err) {
return "Error:" + err
}
}
async componentDidMount() {
this.getMarginData()
this.getOrderbookData()
}
render() {
return (
<div className = "App" >
<Image src={ require('./investsaathilogo.png')}
height = {100} width = {100}/>
<h2> Orderbook Data </h2>
<h5 className="container">
{
console.log('ORDERBOOKNOW' + this.state.orderbook),
this.state.orderbook && this.state.orderbook.map((order, key) =>
<div key={order + key}>
<li>BuySell: {order.BuySell}</li>
</div>
)
}
</h5>
</div>
);
}
}
export default App;
Now I am able to display the data for all clients and all their orders one by one. But React automatically re-renders the data one after the other. Does anyone know how to display all the clients data at once instead of it going through the loop automatically and stopping and displaying the last item of data in the loop?
You can't JSON.stringify something and map. Rather do something like this,
import React, {
Component,
useState,
useEffect
} from 'react';
import './App.css';
import {
Card,
CardImg,
CardText,
CardBody,
CardTitle,
CardSubtitle,
Button
} from 'reactstrap';
import Image from 'react-image-resizer';
import * as firebase from 'firebase';
class App extends Component {
constructor(props) {
super(props);
this.state = {}
}
getMarginData() {
const db = firebase.firestore();
return db.collection('Margin').get().then((querySnapshot) => {
const margins = []
querySnapshot.forEach(doc => {
margins.push(doc.data())
console.log(margins);
});
this.setState({
margins: margins
})
});
}
getOrderbookData() {
const db = firebase.firestore();
return db.collection('Orderbook').get().then((querySnapshot) => {
const orderbooks = []
querySnapshot.forEach(doc => {
orderbooks.push(doc.data())
console.log(orderbooks);
});
this.setState({
orderbooks: orderbooks[0]
})
});
}
componentDidMount() {
this.getMarginData()
this.getOrderbookData()
}
render() {
return (
<div className = "App" >
<Image src={ require('./investsaathilogo.png')} height = {100} width = {100}/>
<p>
<h2> Margin Data </h2>
<h5>
{this.state.margins && this.state.margins.map((margin, index) =>
<div key = {margin.Client1.ALB} > < /div>
)}
</h5>
</p>
<p>
<h2> Orderbook Data </h2>
<card>
<h5> {JSON.stringify(this.state.orderbooks)} </h5>
</card>
</p>
</div>
);
}
}
export default App;

ReactJS -- Unable to find latest title from an api causing error

I have a Landing component and a NewsLatest component. I am hitting on an api and trying to find the article with the latest timestamp but iam unable to get it done in reactJS.I checked the js code its working fine but in react it is not rendering. Kindly suggest something.
import React, { Component } from 'react'
import NewsSearch from '../NewsSearch/NewsSearch';
import NewsLatest from '../NewsLatest/NewsLatest';
import './Landing.css';
import axios from 'axios';
class Landing extends Component {
state={
newsList: []
}
componentDidMount(){
axios.get(`https://api.nytimes.com/svc/topstories/v2/home.json?api-key=7cK9FpOnC3zgoboP2CPGR3FcznEaYCJv`)
.then(res=> {
this.setState({newsList: res.data.results});
});
}
render() {
// console.log(this.state.newsList);
return (
<div className="landing text-center text-white">
<h1>News Portal</h1>
<div className="news-search">
<NewsSearch />
</div>
<div className="news-latest">
<NewsLatest newsList={this.state.newsList}/>
</div>
</div>
)
}
}
export default Landing;
import React, { Component } from 'react';
// import PropTypes from 'prop-types';
class NewsLatest extends Component {
constructor(props){
super(props);
this.state = {
newsTitle:'',
abstract:'',
newsUrl:'',
}
// this.newsLatest = this.newsLatest.bind(this);
}
newsLatest = (e)=>{
// e.preventDefault();
const {newsList} = this.props;
let maxTime = newsList.map(function(o) {
return new Date(o.updated_date);
});
let maximumValue = Math.max(...maxTime);
let latestnews = newsList.filter(function (el) {
return maximumValue === new Date(el.updated_date).getTime();
})[0];
if(latestnews){
this.setState({newsTitle: latestnews.title});
return (<h4>{this.state.newsTitle}</h4>);
}
}
newsTitle = () => (
this.props.newsList.map(item => (<h2 key={item.title}>{item.title}</h2>))
)
render() {
console.log(this.props.newsList);
return (
<div>
<h2>News Latest....</h2>
{this.newsLatest()}
</div>
);
}
}
export default NewsLatest;
There is some issue in rendering in NewsLatest component. KIndly suggest something.
Try this:
You must probably be getting a maximum depth error, use a lifecycle method instead like componentDidUpdate. Update your component state only if the previous props are different from the newer ones.
Read more here: https://reactjs.org/docs/react-component.html
import React, { Component } from "react";
// import PropTypes from 'prop-types';
class NewsLatest extends Component {
constructor(props) {
super(props);
this.state = {
newsTitle: "",
abstract: "",
newsUrl: ""
};
// this.newsLatest = this.newsLatest.bind(this);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.newsList !== this.props.newsList) {
const { newsList } = this.props;
let maxTime = newsList.map(function(o) {
return new Date(o.updated_date);
});
let maximumValue = Math.max(...maxTime);
let latestnews = newsList.filter(function(el) {
return maximumValue === new Date(el.updated_date).getTime();
})[0];
this.setState({ newsTitle: latestnews.title });
}
}
// newsLatest = e => {
// // e.preventDefault();
// const { newsList } = this.props;
// let maxTime = newsList.map(function(o) {
// return new Date(o.updated_date);
// });
// let maximumValue = Math.max(...maxTime);
// let latestnews = newsList.filter(function(el) {
// return maximumValue === new Date(el.updated_date).getTime();
// })[0];
// console.log(latestnews)
// if (latestnews && latestnews.hasOwnProperty('length') && latestnews.length>0) {
// return <h4>{this.state.newsTitle}</h4>;
// }
// };
newsTitle = () =>
this.props.newsList.map(item => <h2 key={item.title}>{item.title}</h2>);
render() {
console.log(this.props.newsList);
return (
<div>
<h2>News Latest....</h2>
<h4>{this.state.newsTitle}</h4>
</div>
);
}
}
export default NewsLatest;
Also, a sandbox: https://codesandbox.io/s/hungry-frog-z37y0?fontsize=14

Modal state with react/redux

I'm managing Todo lists in my app. The main view is a page with all the lists displayed as cards. If you click on one of them, you can modify, update, delete stuff through a modal that appears.
I have a TodoLists reducer that store all the TodoLists. I don't know how to handle the modal. Should I use redux or just local state?
import _ from "lodash";
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { listsActions } from "../duck";
import NewList from "./NewList";
import Card from "./Card";
import Modal from "./Modal";
class Lists extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
list: {}
};
this.hideModal = this.hideModal.bind(this);
this.renderModal = this.renderModal.bind(this);
}
componentDidMount() {
const { fetchByUserId, user } = this.props;
if (user !== undefined) {
fetchByUserId(user.id);
}
}
hideModal() {
this.setState({
modal: false
});
}
renderModal() {
this.setState({
modal: true
});
}
render() {
const { items } = this.props;
const { modal, list } = this.state;
return (
<div>
<NewProject />
<div className="columns">
{_.map(items, (l) => (
<div
key={l.id}
className="column"
>
<Card
list={l}
onClick={() => this.renderModal(l)}
/>
</div>
))}
</div>
<Modal
className={modal ? "is-active" : ""}
list={list}
onClose={this.hideModal}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
const { user } = state.authentication;
const { items, loading, error } = state.lists;
return {
user,
items,
loading,
error
};
};
export default connect(
mapStateToProps,
{ fetchByUserId: listsActions.fetchByUserId }
)(Projects);

trying to pass my arrays (props) into my publish function as selector

import { Mongo } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';
import React, {Component} from 'react';
import {check} from 'meteor/check';
export const Adressen = new Mongo.Collection('Phonebook');
if (Meteor.isServer) {
Meteor.publish('ArrayToExport', function(branches) {
check(branches, [Match.Any]);
if(branches.length > 10){
return this.ready()
};
return Adressen.find(
{branche: {$in: branches}}, {fields: {firmenname:1, plz:1}}
);
});
}
.
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import {Adressen} from "../api/MongoDB";
class ExportArray extends Component{
constructor(props){
super(props);
this.state = {
branches: this.props.filteredBranches
};
}
render(){
return(
<div>
<button onClick={this.exportArrays}></button>+
</div>
);
}
}
export default withTracker( (branches) => {
Meteor.subscribe('ArrayToExport', branches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
this.props.filteredBranche is a pure array,generated through controlled input field. this.props.filteredBranches changes as Input changes, in parent Component.
I thought I was sending my this.props.filteredBranches as an argument through withTracker function. But nothing is passed to the publish function.
if (Meteor.isServer) {
arrayExfct = function (array){
return {
find: {branche:{$in: array }},
fields: {firmenname:1, plz:1}
};
}
Meteor.publish('ArrayToExport', function (array) {
return Adressen.find(
arrayExfct(array).find, arrayExfct(array).fields);
});
}
.
export default withTracker( () => {
arrayExfct = function(array) {
return {
find: {branche: {$in: array}},
fields: {firmenname:1, plz:1}
}
}
var array = ['10555'];
Meteor.subscribe('ArrayToExport', array );
var arrayExfct = Adressen.find(arrayExfct(array).find, arrayExfct(array).fields);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
It would help if you also added an example of where you used this component and how you pass props to it, but I think I see your problem.
You expect the local state in your rendering component to get into the withTracker container, but that would be the other way around. When you make the withTracker container, you are really making another react component that renders your display component (ExportArray) and passes the data (ArrayToExport) down into it.
So, props go like this currently:
external render -> withTracker component -> ExportArray
What you need to do it to get the filteredBranches (which you pass from a parent component?) from the props argument in withTracker and pass that to the subscribtion,
class ExportArray extends Component{
exportArrays () {
const { ArrayToExport } = this.props;
}
render(){
const { ArrayToExport } = this.props;
return(
<div>
<button onClick={this.exportArrays}></button>+
</div>
);
}
}
export default withTracker(propsFromParent => {
const { filteredBranches } = propsFromParent;
Meteor.subscribe('ArrayToExport', filteredBranches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
Hi the issue is with the code below. The parameter called branches is the props so branches.branches is the array you passed in.
export default withTracker( (branches) => {
Meteor.subscribe('ArrayToExport', branches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
Try the following.
export default withTracker( ({branches}) => {
Meteor.subscribe('ArrayToExport', branches);
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
Notice all that changed was
(branches)
became
({branches})
I solved my problem with a combination of Session Variables and State.
//Client
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import {Adressen} from "../api/MongoDB";
import {Meteor} from 'meteor/meteor';
import { Session } from 'meteor/session';
class ExportArray extends Component{
constructor(){
super();
this.state = {
x: [],
y: []
};
this.exportArrays = this.exportArrays.bind(this);
}
exportArrays(e){
e.preventDefault();
this.setState({x: this.props.filteredBranches});
this.setState({y: this.props.filteredPostleitzahlen});
}
render(){
var selector = {branche: {$in: this.state.x},plz: {$in: this.state.y}};
Session.set('selector', selector);
return(
<div>
<button onClick={this.exportArrays}> Commit </button>
</div>
);
}
}
export default withTracker( () => {
const ArrayfürExport = Meteor.subscribe('ArrayToExport', Session.get('selector') );
return {
ArrayToExport: Adressen.find({}).fetch()
};
})(ExportArray);
//Server
Meteor.publish('ArrayToExport', function (selector) {
console.log('von mongodb', selector);
return Adressen.find(
selector
, {
fields: {firmenname:1, plz:1}
});
});
}

Meteor - return inside of a Tracker.autorun function not returning anything

The function renderNotes() is supposed to return the mapped array which is in a separate file. I noticed that when I return some JSX nothing returns to the screen. I think I know the reason why is because it is returning the info to the tracker function. How would I get the info to return to the renderNotes() function while inside the tracker.autorun() function?
import { Meteor } from "meteor/meteor";
import React from "react";
import { withRouter, Link } from "react-router-dom";
import { Accounts } from "meteor/accounts-base";
import { Tracker } from "meteor/tracker";
import SubjectRoutes from "./subjectRoutes/subjectRoutes";
import { Notes } from "../methods/methods"
import Menu from "./Menu.js";
class Home extends React.Component{
componentWillMount() {
if(!Meteor.userId()){
this.props.history.replace("/login")
}
}
logoutUser(e){
e.preventDefault()
Accounts.logout(() => {
this.props.history.push("/login");
});
}
renderNotes(){
Tracker.autorun(function () {
Meteor.subscribe('notes');
let notes = Notes.find().fetch();
// return notes.map((note) => {
// return <p>{note.imageURL}</p>
// })
return <p>asdas</p> //<--Here
});
}
render(){
return (
<div>
<button onClick={this.logoutUser.bind(this)}>Logout</button>
{this.renderNotes()}
<Menu />
</div>
)
}
}
export default withRouter(Home);
Don't know whether this is a proper answer but I like to usually do something like this
import TrackerReact from 'meteor/ultimatejs:tracker-react';
import { Notes } from "../methods/methods";
export default class Home extends TrackerReact(React.Component) {
constructor(props,) {
super(props);
this.state = {
subscription:{
publishNotes: Meteor.subscribe("publish-Notes")
}
};
}
returnNotes(){
return Notes.find().fetch();
}
render(){
...
const stuff = this.returnNotes().map((note)=>{
return <p>{note}</p>
});
return (
....
{stuff}
)
}
}
This worked for me:
import { Meteor } from "meteor/meteor";
import React from "react";
import { withRouter, Link } from "react-router-dom";
import { Accounts } from "meteor/accounts-base";
import { Tracker } from "meteor/tracker";
import SubjectRoutes from "./subjectRoutes/subjectRoutes";
import { Notes } from "../methods/methods"
import Menu from "./Menu.js";
class Home extends React.Component{
constructor(props){
super(props)
this.state = {
notes: []
}
}
componentWillMount() {
if(!Meteor.userId()){
this.props.history.replace("/login")
}
this.tracker = Tracker.autorun(()=>{
Meteor.subscribe('notes');
let notes = Notes.find().fetch();
this.setState({ notes })
});
}
componentWillUnmount() {
this.tracker.stop();
}
logoutUser(e){
e.preventDefault()
Accounts.logout(() => {
this.props.history.push("/login");
});
}
renderNotes(notes){
return notes.map((note) => {
return (
<div key={note._id}>
<img src={note.imageURL} />
<p>{note.type}</p>
</div>
)
});
}
render(){
return (
<div>
<button onClick={this.logoutUser.bind(this)}>Logout</button>
<Menu />
{this.renderNotes(this.state.notes)}
</div>
)
}
}
export default withRouter(Home);

Resources