Building a modal component that opens up a bootstrap modal from any part of the app then sets custom states for that component outside of it. It works fine but i always just get this error once i open the modal and I cant seem to figure out why:
Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.` Doesnt really break anything but error keeps showing up.
My code:
layout.js
import React from "react";
import {Link} from 'react-router';
import NotificationSystem from 'react-notification-system';
import AppHeader from "#/ui/header/AppHeader";
import AppFooter from "#/ui/footer/AppFooter";
import Modal from "#/ui/modals/modal/Modal";
import "#/main.scss";
import './layout.scss';
export default class Layout extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
app.notify.clear = this.refs.notificationSystem.clearNotifications;
app.notify = this.refs.notificationSystem.addNotification;
app.modal = this.refs.modal.updateProps;
}
render() {
return (
<div class="app">
<div class="header">
<AppHeader page={this.props.location.pathname.replace('/', '')}/>
</div>
<div class="body">
{this.props.children}
</div>
<div class="footer">
<AppFooter />
</div>
<NotificationSystem ref="notificationSystem" style={false} />
<Modal ref="modal" />
</div>
);
};
}
Modal.js
import React from "react";
import ReactDOM from 'react-dom';
import SVGInline from "react-svg-inline";
import {closeSvg} from '#/utils/Svg';
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
showHeader: true,
showFooter: false,
title: "",
size: '',
className: '',
id: '',
footerContent: null,
showSubmitBtn: true,
showCancelBtn: true,
cancelBtnText: "Cancel",
successBtnText: "Save Changes",
onModalClose: () => {},
showModal: false,
html: () => {}
}
this.updateProps = this.updateProps.bind(this);
this.hideModal = this.hideModal.bind(this);
}
componentWillMount() {
var self = this;
var $modal = $(ReactDOM.findDOMNode(this));
}
componentDidUpdate(prevProps, prevState) {
if(this.state.showModal) {
$('body').addClass('modal-open');
} else {
$('body').removeClass('modal-open');
}
}
componentWillUnmount() {
// $('body').removeClass("modal-open");
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
updateProps(args) {
let merged = {...this.state, ...args};
this.setState(merged);
}
hideModal() {
this.setState({
showModal: false
});
this.state.onModalClose();
}
buildFooter() {
if(this.props.footerContent) {
return (
<div class="content">
{this.props.footerContent}
</div>
)
} else if(this.props.showCancelBtn && this.props.showSubmitBtn) {
return (
<div class="buttons">
<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>{this.props.cancelBtnText}</button>
<button type="button" class="btn btn-success">{this.props.successBtnText}</button>
</div>
);
} else if(this.props.showCancelBtn) {
return (<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>Close</button>);
} else if(this.props.showSubmitBtn) {
return (<button type="button" class="btn btn-success">Save changes</button>);
}
}
render() {
let {
id,
className,
onModalClose,
size,
showHeader,
title,
children,
showFooter,
showModal,
html
} = this.state;
return (
<div class={`modal-wrapper`} >
{
showModal ?
<div class={`modal fade in ${className}`} role="dialog">
<div class="bg" ></div>
<div class={`modal-dialog ${size}`}>
<div class="modal-content">
{ showHeader ?
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<SVGInline svg={closeSvg} />
</button>
<h4 class="modal-title">{ title }</h4>
</div> : '' }
<div class="modal-body" >
{html()}
</div>
{ showFooter ?
<div class="modal-footer">
{ this.buildFooter() }
</div> : ''
}
</div>
</div>
</div>
: ''
}
</div>
);
}
}
SelectDefaultImage.js
import React from "react";
import sass from "./selectdefaultimage.scss";
import FullScreenImageModal from "#/ui/modals/fullscreenimagemodal/FullScreenImageModal";
export default class SelectDefaultImage extends React.Component {
constructor() {
super();
this.state = {
showModal: false,
imgUrl: false,
}
}
showImageModal(image) {
this.setState({
showModal: true,
imgUrl: image
});
}
hideImageModal() {
this.setState({
showModal: false,
imgUrl: false
})
}
onSelectImageClick(e, image) {
$('.select-image-widget .active').removeClass('active');
$(e.target).parent().addClass('active');
// this.props.selectedImage(image)
}
render() {
let {listingManager, images, selectedImage} = this.props;
let {imgUrl} = this.state;
return (
<div class="content">
<div class="row">
<div class="col-sm-12">
<label class="control-label" for="description">Select an Image</label>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="select-image-widget">
{
images.map((image, idx) => {
return (
<div class="selecter" key={idx}>
<div class="img" style={{backgroundImage: `url(${listingManager.LISTINGS_PATH + image})` }} onClick={(e) => { this.onSelectImageClick(e, image) }}></div>
<i class="fa fa-search-plus" aria-hidden="true" onClick={()=> {this.showImageModal(image)}}></i>
</div>
)
})
}
</div>
</div>
</div>
{
this.state.showModal ?
app.modal({
showModal: true,
className: "fullscreen-image-modal",
size: "modal-lg",
html: () => {
return (<img src={listingManager.LISTINGS_PATH + imgUrl} />);
}
})
: ''
}
</div>
)
}
}
The reason for the error is most likely that in SelectDefaultImage, you call app.modal from within the render method, and app.modal is this.refs.modal.updateProps, which does a setState. If you put the app.modal call in showImageModal, I expect the error to go away. However, setting the state of a another component by means of refs and globals is a bit of a React antipattern, so I would recommend to do some refactoring and use props to pass the data.
Related
I would like to ask question about some kind of grid gallery for a portfolio section. Each grid has a overlay which could be triggered by mouseover and the grids are generated by a map function and data in the grids are linked with a json file. The problem is that when I put a draft modal that I supposed it is for the grid , an error message showed "TypeError: Cannot read property 'toggleModal' of undefined" for this tag . Thanks for any solution.
import React, { Component } from 'react';
import Modal from './Modal';
class Portfolio extends Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
}
toggleModal = () => {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
if(this.props.data){
var projects = this.props.data.projects.map(function(projects){
var projectImage = 'images/portfolio/'+projects.image;
return <div key={projects.title} className="columns portfolio-item">
<div className="item-wrap">
<a href={projects.url} className="open-popup" title={projects.title}>
<img alt={projects.title} src={projectImage} />
<div className="overlay">
<div className="portfolio-item-meta">
<h5>{projects.title}</h5>
<p>{projects.category}</p>
</div>
</div>
<div className="link-icon"><i className="fa fa-link"></i></div>
</a>
</div>
<button onClick={this.toggleModal}>
Open the modal
</button>
<Modal show={this.state.isOpen} onClose={this.toggleModal} >
Here's some content for the modal
</Modal>
</div>
})
}
return (
<section id="portfolio">
<div className="row">
<div className="twelve columns collapsed">
<h1>Check Out Some of My Works.</h1>
<div id="portfolio-wrapper" className="bgrid-quarters s-bgrid-thirds cf">
{projects}
</div>
</div>
</div>
</section>
);
}
}
export default Portfolio;
and the Modal.js is below:
import React from 'react';
import PropTypes from 'prop-types';
class Modal extends React.Component {
render() {
// Render nothing if the "show" prop is false
if(!this.props.show) {
return null;
}
// The gray background
const backdropStyle = {
position: 'fixed',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0,0,0,0.3)',
padding: 50
};
// The modal "window"
const modalStyle = {
backgroundColor: '#fff',
borderRadius: 5,
maxWidth: 500,
minHeight: 300,
margin: '0 auto',
padding: 30
};
return (
<div className="backdrop" style={backdropStyle}>
<div className="modal" style={modalStyle}>
{this.props.children}
<div className="footer">
<button onClick={this.props.onClose}>
Close
</button>
</div>
</div>
</div>
);
}
}
Modal.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool,
children: PropTypes.node
};
export default Modal;
The error is caused from this line:
var projects = this.props.data.projects.map(function(projects){
Since you used this.toggleModal inside this function, the this context is linking to this function, not the React component.
The solution is to use an arrow function like this:
var projects = this.props.data.projects.map((projects) => {
On a side note, it's not a good idea to define a variable inside a block and use it outside of it.
This is my revised version, thanks. What should I do if I want to add next and previous function in modal in order to show next or previous project information? Thanks. I am sorry that I am new with React and Javascript.
import React, { Component } from 'react';
import Modal from './Modal';
class Portfolio extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
activeProjects:"",
activeProjectImage:""
};
}
toggleModal = (projects,projectImage) => {
this.setState({activeProjects:projects, activeProjectImage:projectImage},() =>
this.setState({
isOpen: !this.state.isOpen
}));
}
render() {
if(this.props.data){
var projects = this.props.data.projects.map((projects) => {
var projectImage = 'images/portfolio/'+projects.image;
return <div key={projects.title} className="columns portfolio-item">
<div className="item-wrap">
<a onClick={() => this.toggleModal(projects,projectImage)} className="open-popup" title={projects.title}>
<img alt={projects.title} src={projectImage} />
<div className="overlay">
<div className="portfolio-item-meta">
<h5>{projects.title}</h5>
<p>{projects.category}</p>
</div>
</div>
<div className="link-icon"><i className="fa fa-link"></i></div>
</a>
</div>
</div>
})
}
return (
<section id="portfolio">
<div className="row">
<div className="twelve columns collapsed">
<h1>Check Out Some of My Works.</h1>
<div id="portfolio-wrapper" className="bgrid-quarters s-bgrid-thirds cf">
{projects}
<Modal show={this.state.isOpen} onClose={this.toggleModal} >
<img alt={this.state.activeProjects.title} src={this.state.activeProjectImage} />
</Modal>
</div>
</div>
</div>
</section>
);
}
}
export default Portfolio;
I have similar problem like this How to use Bootstrap Modal inside map function in React Js to render indivisual elements?
But answer is not working in my code or I didn't implement it properly. I know that in map to method is directed only last element of list but what I can do to transfer in toogleConfirmation proper data from map? Any hints?
Its my ModalComponent.jsx
export const ModalHeader = props => {
return <div className="modal-header">{props.children}</div>;
};
export const ModalBody = props => {
return <div className="modal-body">{props.children}</div>;
};
export const ModalFooter = props => {
return <div className="modal-footer">{props.children}</div>;
};
class ModalComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
modalShow: '',
display: 'none'
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({
modalShow: 'show',
display: 'block'
});
}
closeModal() {
this.setState({
modalShow: '',
display: 'none'
});
}
componentDidMount() {
this.props.isOpen ? this.openModal() : this.closeModal();
}
componentDidUpdate(prevProps) {
if (prevProps.isOpen !== this.props.isOpen) {
this.props.isOpen ? this.openModal() : this.closeModal();
}
}
render() {
return (
<div
className={'modal fade ' + this.state.modalShow}
tabIndex="-1"
role="dialog"
aria-hidden="true"
style={{ display: this.state.display }}
>
<div className="modal-dialog" role="document">
<div className="modal-content">{this.props.children}</div>
</div>
</div>
);
}
}
export default ModalComponent;
and table in render method
class Datas extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
isConfirmed: false,
collections: [],
data: '',
datas: []
};
this.toggle = this.toggle.bind(this);
this.changeConfirmation = this.changeConfirmation.bind(this);
}
toggle() {
this.setState({ modal: !this.state.modal });
}
changeConfirmation(){
this.setState({
isConfirmed: !this.state.isConfirmed
});
}
toggleConfirmation(name, status) {
this.changeConfirmation();
this.toggle();
//next code with name and status
}
render() {
return (
<Table responsive>
<thead className="text-primary">
<tr>
<th>Name</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{this.state.collections.map((collection, i) => (
<tr key={i}>
<td>{collection.name}</td>
<td>{collection.status}</td>
<td>
<button
type="button"
className="btn btn-danger"
onClick={() => this.setState({
modal: true,
data: collection
})}
>
</button>
<Modal isOpen={this.state.modal}>
<ModalHeader>
<h5>Confirmation</h5>
<button
type="button"
className="close"
aria-label="Close"
onClick={this.toggle.bind(this)}
>
<span aria-hidden="true">×</span>
</button>
</ModalHeader>
<ModalBody>
<p>Are you sure to proceed with this action with {collection.name} ?</p>
</ModalBody>
<ModalFooter>
<button
type="button"
className="btn btn-secondary"
onClick={this.toggle.bind(this)}
>
Close
</button>
<button
type="button"
className="btn btn-primary"
onClick={this.toggleConfirmation.bind(this, collection.name, collection.status)}
>
Ok
</button>
</ModalFooter>
</Modal>
</td>
</tr>
))}
</tbody>
</Table>
)
}}
just pass collection as props to ModalBody , ModalHeader & ModalFooter in Data.js
<ModalBody collection={collection}/>
you can update ModalBody , ModalHeader & ModalFooter like this :
export const ModalBody = props => {
return (<div className="modal-body">
<p>Are you sure to proceed with this action with {props.collection.name} </p>
</div>);
};
The toaster component is made programatically and i am passing the message through another component.
my Toaster component Looks like this:-
export default class MyToaster extends React.Component {
constructor(props) {
super(props);
this.toaster = React.createRef();
this.state = {
message: [],
show: false
};
this.state.message = this.props.message;
}
handleClose() {
this.setState({show: false})
}
createtoaster() {
let toastmessage = [];
if ( this.state.show === true) {
for (let i = 0; i <= this.state.message.length; i++) {
let tmessage = <div className="col-md-3 offset-md-8">
<div className="card-header">
<h3 className="card-title">Toast</h3>
</div>
<div className="card-body">
{this.state.message[i]}
</div>
<div className="card-footer">
<button className="btn btn-primary" onClick={this.handleClose()}>x</button>
</div>
</div>
toastmessage.push(tmessage);
}
return (toastmessage);
}
}
render() {
return (
<div className="col-md-2 offset-md-9">
<button className="btn btn-primary" onClick={() => this.setState({show:true})}>show Toaster</button>
</div>
)
}
}
Also this is my PostCard.js page in which the toaster component is called and message is passed.
export default class MyCard extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPage: this.props.pgNo,
details: [],
id: null,
index: 0
}
this.message = 'osihfosihfoi';
}
AddButton() {
return (
<Link to={`${this.props.url}/addnew${this.props.url}`}>
<button
style={{
float: "right"
}}
className="btn btn-primary"><Faplus/>
</button>
</Link>
);
}
closeAfter7 = () => toast("7 Kingdoms", {autoClose: 7000});
fetchMoreData = () => {
if(this.state.index<100){
this.setState({
index: this.state.index + 5
})
}}
componentDidMount() {
window.addEventListener('scroll', this.onScroll);
this.fetchMoreData();
}
onScroll = () => {
$(window).scroll(() => {
if ($(window).scrollTop() + $(window).height() == $(document).height()) {
this.fetchMoreData();
}
});
}
createCard = () => {
let cardBody = [];
for (let i = this.state.currentPage; i < this.state.index; i++) {
let card = <div className="content">
<div className="container-fluid">
<div className="col-md-6 offset-md-3">
<div className="card">
<div className="card-header">
<h3 className="card-title"></h3>
</div>
<div className="card-body">
<h5>
ID :
</h5>{this.props.data[i].id}<br/>
<h5>
User ID :
</h5>{this.props.data[i].userId}<br/>
<h5>
Title :
</h5>{this.props.data[i].title}<br/>
<h5>
Body :
</h5>{this.props.data[i].body}<br/>
</div>
<div className="card-footer clearfix"></div>
</div>
</div>
</div>
</div>
cardBody.push(card);
}
return (cardBody)
}
render() {
return (
<div className="row">
<div className="col-md-2 offset-md-10">{this.AddButton()}
</div>
<Toaster message={this.message}/>
{/* <div id="snackbar" style={{backgroundColor: "red"}}>Hogaya.</div> */}
<div>
{this.createCard()}
</div>
</div>
)
}
}
My UI renders the Show toaster button but not do anything when it is clicked. Also it dosent give any errors. Can't figure out the problem so if anyone can point out what i am doing wrong it ll be great. Also Please let me know if I am not using the correct method or logic.
TIA.
It was not rendering anything because I wasn't rendering my createtoaster component. the correct way is that in my toaster component i should render it like this
render() {
return (
<div className="col-md-2 offset-md-9">
<button className="btn btn-primary" onClick={this.handleOpen}></button>
{this.createtoaster()}
</div>
)
}
}
I'm following the example here: https://redux-form.com/6.6.3/examples/wizard/
I've managed to make everything I needed up until now, but I'm getting stuck on how to make custom radio buttons.
This is what I have so far, but I can't get the radio button to change when clicking on the divs that represent it
import React, { Component } from 'react'
import './pictureCard.css'
import '../../../../app.css'
import MaterialIcon from 'react-google-material-icons'
import { Field } from 'redux-form'
export default class PictureCard extends Component {
constructor(props) {
super(props)
this.state = {
isChecked: false
}
}
handleTick = event => {
const card = event.target
card.classList.toggle('question__checkbox--selected')
this.setState(prevState => {
return { isChecked: !prevState.isChecked }
})
}
render() {
const {
tabOrder,
cardName,
cardKey,
cardLabel,
thumbnail,
thumbnailAlt
} = this.props
return (
<li
className="question__choice"
tabIndex={tabOrder}
onClick={this.handleTick}
>
<Field
name={cardName}
type="radio"
component="input"
value={cardLabel}
checked={this.state.isChecked}
/>
<div className="question__tick-wrap">
<MaterialIcon icon="check" />
</div>
{thumbnail === undefined ? null : (
<div className="question__image-wrap">
<img src={thumbnail} alt={thumbnailAlt} />
</div>
)}
<div className="question__text-wrap flex flex--center-vertical">
<div className="question__label">
<div className="question__letter">
<span>{cardKey}</span>
</div>
</div>
<div className="question__text-label">{cardLabel}</div>
</div>
<div className="question__bg" />
</li>
)
}
}
I can programatically change the value, but it doesn't update in the store, and I don't know why :(
This is what I tried:
import React, { Component } from 'react'
import './pictureCard.css'
import '../../../../app.css'
import MaterialIcon from 'react-google-material-icons'
import { Field } from 'redux-form'
export default class PictureCard extends Component {
constructor(props) {
super(props)
this.state = {
valueTest: false
}
}
handleTick = event => {
const card = event.target
card.classList.toggle('question__checkbox--selected')
this.setState({ valueTest: 'Hello' })
}
render() {
const {
input,
tabOrder,
cardName,
cardKey,
cardLabel,
thumbnail,
thumbnailAlt
} = this.props
return (
<li
className="question__choice"
tabIndex={tabOrder}
onClick={this.handleTick}
>
<input
{...input}
name={cardName}
value={this.state.valueTest}
type="text"
tabIndex={tabOrder}
/>
<div className="question__tick-wrap">
<MaterialIcon icon="check" />
</div>
{thumbnail === undefined ? null : (
<div className="question__image-wrap">
<img src={thumbnail} alt={thumbnailAlt} />
</div>
)}
<div className="question__text-wrap flex flex--center-vertical">
<div className="question__label">
<div className="question__letter">
<span>{cardKey}</span>
</div>
</div>
<div className="question__text-label">{cardLabel}</div>
</div>
<div className="question__bg" />
</li>
)
}
}
I have two components: CardContainer and SingleVideoContainer.
CardContainer will contain multiple SingleVideoContainers based on the data in the database.
import React, { Component } from 'react';
import Modal from 'boron/WaveModal';
//Default firebase App
import * as firebase from 'firebase';
import { firebaseApp } from '../firebase/firebase';
import SingleCardContainer from '../cards/SingleCardContainer';
var dataRef = firebaseApp.database();
var dataArray = [];
class CardContainer extends Component {
constructor(props) {
super(props);
this.state = {
userid: "",
videoLink: "",
likes: "",
challenges: "",
videoCat: "",
videoDesc: "",
videoTitle: "",
profilePic: "",
disikes: ""
}
this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
}
showModal() {
this.refs.modal.show();
}
hideModal() {
this.refs.modal.hide();
}
componentDidMount() {
}
render() {
function initApp() {
var videosRef = dataRef.ref('posts/');
videosRef.on('value', function (snapshot) {
snapshot.forEach(function (data) {
var userInfo = {};
var userArray = [];
//Set up the next user array group
//9 items for 1 user.
userInfo.userid = data.val().userid;
userInfo.likes = data.val().likes;
userInfo.dislikes = data.val().dislikes;
userInfo.challenges = data.val().challenges;
userInfo.profilePic = data.val().profilePic;
userInfo.videoCategory = data.val().videoCategory;
userInfo.videoDesc = data.val().videoDesc;
userInfo.videoTitle = data.val().videoTitle;
userInfo.videoURL = data.val().videoURL;
//Then save the object in the array
userArray.push(userInfo);
//change the index to next user.
})
});
}
/**
* This loads when the page loads (right before renders)
*/
window.addEventListener('load', function () {
initApp()
});
return (
<div id="bodyType">
{
userArray.map(
data => <SingleCardContainer
userid={data.userid}
title={data.title}
likes={data.likes}
dislikes={data.challenges}
videoURL={data.videoURL}
/>)
}
</div>
)
}
}
export default CardContainer;
I want to render the SingleCardContainer by passing in the information from the dataArray as props. Each user has 9 items that I need to pass in as props.
How would I do that? I can render 1 but not multiple ones.
Here is the SingleCardContainer:
import React, { Component } from 'react';
import {
Player, ControlBar,
ForwardControl, CurrentTimeDisplay,
TimeDivider, VolumeMenuButton, BigPlayButton
} from 'video-react';
import ModalContainer from '../cards/ModalContainer';
class SingleCardContainer extends Component {
constructor(props) {
super(props);
this.likeButton = this.likeButton.bind(this);
this.challengeButton = this.challengeButton.bind(this);
this.dislikeButton = this.dislikeButton.bind(this);
}
likeButton() {
}
challengeButton() {
}
dislikeButton() {
}
//We will have to pass down the states from CardContainer as props to this so that they get updated in real-time *fingers-crossed*
render() {
return (
<div className="container">
<div className="card" id="generalCard">
<div className="card-text">
<div id="singleVideoContainer">
<h3>{this.props.title}</h3><p> {this.props.userid}</p>
<Player poster="" src={this.props.videoURL}></Player>
<div id="videoInfoSection">
<div className="row" id="buttonContainerRow">
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={() => this.likeButton()} role="button" href="#"><i className="fa fa-thumbs-up"></i></a>
<p>{this.props.likes}</p>
</div>
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={() => this.challengeButton()} role="button" href="#"><i className="fa fa-shield"></i></a>
<p>{this.props.challenges}</p>
</div>
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={() => this.dislikeButton()} role="button" href="#"><i className="fa fa-thumbs-down"></i></a>
<p>{this.props.dislikes}</p>
</div>
</div>
<div id="commentSection">
<p>{this.props.videoDesc}</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default SingleCardContainer;
You should put any data calls into componentDidMount and never in render. Also note that you can simply use map to convert the snapshot array to the values. Then call setState which will eventually call render with the new data.
import React from 'react';
import * as firebase from 'firebase';
import { firebaseApp } from '../firebase/firebase';
class CardContainer extends React.Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
componentDidMount() {
var dataRef = firebaseApp.database();
var videosRef = dataRef.ref('posts/');
videosRef.on('value', (snapshot) => {
var data = snapshot.map(o => o.val());
this.setState({ data: data });
});
}
render() {
const { data } = this.state;
return (<div>
{data.map(o => <SingleCardContainer {...o} />)}
</div>);
}
}
class SingleCardContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
const {title, userid, videoURL, videoDesc, likes, dislikes, challenges} = this.props;
return (
<div className="container">
<div className="card" id="generalCard">
<div className="card-text">
<div id="singleVideoContainer">
<h3>{title}</h3>
<p>{userid}</p>
<p>{videoURL}</p>
<div id="videoInfoSection">
<div className="row" id="buttonContainerRow">
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={console.log} role="button" href="#"><i className="fa fa-thumbs-up"></i></a>
<p>{likes}</p>
</div>
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={console.log} role="button" href="#"><i className="fa fa-shield"></i></a>
<p>{challenges}</p>
</div>
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={console.log} role="button" href="#"><i className="fa fa-thumbs-down"></i></a>
<p>{dislikes}</p>
</div>
</div>
<div id="commentSection">
<p>{videoDesc}</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
Below I have a working code snippet where I stripped out Firebase and replaced it with hard-coded data to prove that this will work with React.
class CardContainer extends React.Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
componentDidMount() {
const data = [
{title: 'Title1', userid:'u1', videoURL:'video1.webm', videoDesc:'desc1', likes: 'like1', dislikes: 'dislike1', challenges: 'challenge1'},
{title: 'Title2', userid:'u2', videoURL:'video2.webm', videoDesc:'desc2', likes: 'like2', dislikes: 'dislike2', challenges: 'challenge2'},
{title: 'Title3', userid:'u3', videoURL:'video3.webm', videoDesc:'desc3', likes: 'like3', dislikes: 'dislike3', challenges: 'challenge3'},
];
this.setState({ data: data });
}
render() {
const { data } = this.state;
return (<div>
{data.map(o => <SingleCardContainer {...o} />)}
</div>);
}
}
class SingleCardContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
const {title, userid, videoURL, videoDesc, likes, dislikes, challenges} = this.props;
return (
<div className="container">
<div className="card" id="generalCard">
<div className="card-text">
<div id="singleVideoContainer">
<h3>{title}</h3>
<p>{userid}</p>
<p>{videoURL}</p>
<div id="videoInfoSection">
<div className="row" id="buttonContainerRow">
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={console.log} role="button" href="#"><i className="fa fa-thumbs-up"></i></a>
<p>{likes}</p>
</div>
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={console.log} role="button" href="#"><i className="fa fa-shield"></i></a>
<p>{challenges}</p>
</div>
<div className="col-md-4 col-xs-6 col-sm-4">
<a className="supportButtons" onClick={console.log} role="button" href="#"><i className="fa fa-thumbs-down"></i></a>
<p>{dislikes}</p>
</div>
</div>
<div id="commentSection">
<p>{videoDesc}</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(<CardContainer />, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div><!--REACT ROOT--></div>
However, I am not as familiar with Firebase so any bugs you run into would be due to the data coming from Firebase which I don't have control over and might require a different question.