Child component not re-rendering when Parent component re-renders - javascript

I'm using redux for state management in my app. But after I dispatch an action only the parent component updates the children doesn't receive the new props or updates. I'm using react 16.2.0, Redux 3.7.2, react-redux 5.0.6. These are my components:
Parent component
class ConnectingItems extends React.Component{
constructor(props){
super(props);
this.afterProds = this.afterProds.bind(this);
this.handleSearch = this.handleSearch.bind(this);
this.data = this.props.data.products;
}
componentDidUpdate(){
this.data = this.props.data.products;
}
afterProds(){
this.data = this.props.data.products;
this.forceUpdate();
}
handleSearch(data){
this.data = data;
this.forceUpdate();
}
render(){
const products = this.props.data.products;
const searched_products = this.data;
console.log('rerendering in main')
return(
<div>
<Navbar/>
<div className="container">
<h5 className="center-align">
<Searchform data={products} new_data={this.handleSearch}/>
</h5>
</div>
<ProductsList user_data={searched_products}/>
<Modal
header='Add Product'
modalOptions={{
opacity: .0
}}
trigger={
<div className='fixed-action-btn action-button'>
<a className="btn-floating btn-large yellow darken-1">
<i className="fa fa-plus"></i>
</a>
</div>
}>
<AddProducts afterProdAdd={this.afterProds}/>
</Modal>
</div>
);
}
}
Child componenet:
class ConnectingProductListing extends React.Component{
constructor(props){
super(props);
this.handleDelete = this.handleDelete.bind(this);
this.afterEdit = this.afterEdit.bind(this);
}
handleDelete(id){
this.props.deleteProduct(id);
console.log('delete dispatched');
this.forceUpdate();
}
componentWillReceiveProps(newprops){
console.log(newprops);
}
afterEdit(){
this.forceUpdate();
}
render(){
let data = this.props.user_data;
console.log('im re rendering')
console.log(data);
return(
<div className="container section">
<div className="row">
<div className="col s12">
{data.length < 1 ?
<div className="col s12 center-align">
<p>
<b>No Product here.</b>
</p>
</div>:
data.map(product => {
const name = product.name;
const quantity = product.quantity;
const price = product.price;
return(
<div key={product.id} className="card center grey lighten-5 z-depth-1">
<div className='card-content left-align'>
<span className='card-title'>
{name}
</span>
<span>
Price: ${price}<br/>
</span><span>
Quantity: {quantity}
</span>
</div>
<div className='card-action center'>
<div className='row'>
<div className='col s12'>
<Modal
header='Edit Product'
modalOptions={{
opacity: 0.0
}}
trigger={
<button className='btn yellow accent-3 center'>Edit</button>
}
actions={
<div>
<Modal
header='Delete Product'
modalOptions={{
opacity: 0.0
}}
trigger={
<a className="modal-action modal-close waves-effect waves-yellow btn-flat">Delete</a>
}
actions={
<div>
<a className='modal-action modal-close waves-effect waves-yellow btn-flat'
onClick={() => this.handleDelete(product.id)}>Yes</a>
<a className='modal-action modal-close waves-effect waves-yellow btn-flat'>No</a>
</div>
}>
<p>Are you sure you want to delete this product? It can't be undone</p>
</Modal>
<a className="modal-action modal-close waves-effect waves-yellow btn-flat">Close</a>
</div>
}>
<EditProducts product={product} afterEdit={this.afterEdit}/>
</Modal>
</div>
</div>
</div>
</div>
);
})
}
</div>
</div>
</div>
);
}
}
My reducer:
const rootReducer = (state = initialState, action) => {
switch (action.type){
case constants.ADD_PRODUCT:
return {
...state,
data: {
...state.data,
products: [
...state.data.products,
action.payload
]
}
}
default:
return state;
}
};
export default rootReducer;
Initial state:
initialState = {
data: {
id: 0,
name: 'John Doe',
email: 'johndoe#gmail.com',
products: [
{
id: 0,
name: 'product name',
price: 10,
quantity: 10,
}
],
}
};
store:
import { createStore } from "redux";
import rootReducer from "../reducer/index";
const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
also i'm only focusing on the add product action
Please guys help me

UPDATE: I see you added some code, but you're still missing the container? And your action (constant) definition code?
If you're using redux, then you should include the source code for your store and containers.
Most likely the issue is the first one at https://web.archive.org/web/20180304224831/https://redux.js.org/troubleshooting#nothing-happens-when-i-dispatch-an-action:
Redux assumes that you never mutate the objects it gives to you in the reducer. Every single time, you must return the new state object.
There are various suggestions there. If you include more code, I might be able to help more.

Related

localStorage gets reset after button click ReactJS

I'm currently experiencing an issue where local storage is reset after trying to add an item that belongs to a different page(ex shown below) to the local storage. The local storage functionality is being used for a shopping cart feature.
It all works well when I try adding items from the same category, but once I switch the the category the local storage is reset.
A weird behavior that I also noticed is that for the first item that I try to add to the cart, I have to double click it for it to register in the local storage.
I've set the program so that only the shopping cart page needs to access the local storage.
Adding product from "Wearables category"
Going back and into the Items in the "Computers" section. Ignore sidebar
Adding item from wearable section and local storage is cleared.
Code:
App.js
class App extends Component {
userData;
constructor(props) {
super(props);
this.state = {
cart: [],
};
this.handleAddToCart = this.handleAddToCart.bind(this);
}
handleAddToCart = (productId, prodName, description, price) => {
console.log(" Handle Add to Cart Called ", productId);
console.log("->cart state: ", this.state.cart);
const holder = {
productId,
quantity: 1,
prodName,
description,
price,
};
const idx = this.indexOfProduct(productId);
if (idx == -1) {
// Product does not exist in cart
this.setState(
{
cart: [...this.state.cart, holder],
},
() => {
console.log("Updated Cart: ", this.state.cart);
}
);
} else {
let newArray = [...this.state.cart];
newArray[idx] = {
...newArray[idx],
quantity: newArray[idx].quantity + 1,
};
this.setState(
{
cart: newArray,
},
() => {
console.log("Updated Cart: ", this.state.cart);
}
);
}
localStorage.setItem("cart", JSON.stringify(this.state.cart));
};
indexOfProduct(productId) {
for (let index = 0; index < this.state.cart.length; index++) {
if (this.state.cart[index].productId == productId) return index;
}
return -1;
}
render() {
return (
<div className="App">
{/* <div className="container-fluid">
<NavBarComponent />
</div> */}
<>
<Router>
<div className="container-fluid">
<NavBarComponent />
</div>
<Switch>
<Route exact path="/sidebar">
<SideBarComponent />
</Route>
<Route exact path="/products/:category">
<ProductGridComponent />
</Route>
<Route exact path="/cart">
<ShoppingCartComponent />
</Route>
<Route exact path="/product/:id">
{/*onAddToCart={this.handleAddToCart} */}
<ProductViewComponent onAddToCart={this.handleAddToCart} />
</Route>
<Route exact path="/contact">
<ContactUsComponent />
</Route>
<Route exact path="/about-us">
<AboutUsComponent />
</Route>
<Route exact path="/">
<HomeComponent />
</Route>
</Switch>
</Router>
</>
<FooterComponent />
</div>
);
}
}
export default App;
ShoppingCartComponent.jsx
class ShoppingCartComponent extends Component {
constructor(props) {
super(props);
this.state = {
cart: [],
};
console.log("Hello Im the constructor");
}
static getDerivedStateFromProps(props, state) {
console.log("Hello Im the dState Func");
const sCart = localStorage.getItem("cart");
const parsedCart = JSON.parse(sCart);
if (sCart == null) {
return { cart: [] };
} else {
console.log("cart String mount on shopping cart: ", sCart);
console.log("cart Object at mount on shopping cart: ", parsedCart);
return { cart: parsedCart };
console.log("After appending", this.state.cart);
}
}
render() {
console.log("Shopping Cart Array at Render(): ", this.state.cart);
return (
<div className="container mt-5 p-3 rounded cart">
<div className="row no-gutters">
<div className="col-md-8">
<div className="product-details mr-2">
<div className="d-flex flex-row align-items-center">
<i className="fa fa-arrow"></i>
<button /* onClick={history.back} */>
<span className="ml-2">
<a style={{ color: "black" }}>Continue Shopping</a>
</span>
</button>
</div>
<hr />
<h6 className="mb-0">Shopping cart</h6>
<div className="d-flex justify-content-between">
<span>
You have {this.state.cart.length} items in your cart
</span>
<div className="d-flex flex-row align-items-center">
<span className="text-black-50">Sort by:</span>
<div className="price ml-2">
<span className="mr-1">price</span>
<i className="fa fa-angle-down"></i>
</div>
</div>
</div>
{this.state.cart.map((product) => (
<div className="d-flex justify-content-between align-items-center mt-3 p-2 items rounded">
<div className="d-flex flex-row">
<img
className="rounded"
src="https://i.imgur.com/QRwjbm5.jpg"
width="40"
/>
<div className="ml-2">
<span className="font-weight-bold d-block">
{product.prodName}
</span>
<span className="spec">256GB, Navy Blue</span>
</div>
</div>
...
ProductGridComponent.jsx //Where the products per categories are displayed. Sidebar is a separate component.
class ProductGridComponent extends Component {
constructor(props) {
super(props);
const windowUrl = window.location.pathname.substring(1);
console.log("window url: ", windowUrl);
this.state = {
category: windowUrl.substring(windowUrl.indexOf("/") + 1), //Please fix me, I am vulnerable to SQL Injection
products: [],
};
console.log(this.state.category);
this.handleShopButtonClick = this.handleShopButtonClick.bind(this);
}
componentDidMount() {
ProductService.getProductsByCategory(this.state.category).then((res) => {
this.setState({ products: res.data });
});
}
handleShopButtonClick(productId) {
this.props.history.push(`/product/${productId}`);
}
onAddClick() {}
render() {
return (
<>
{/* <div className="container-fluid page-body-wrapper"> */}
<div className="wrapper">
<SideBarComponent />
<div className="row" style={{ marginLeft: "5px" }}>
{this.state.products.map((product) => (
<div className="col col-md-3" style={{ marginTop: "5px" }}>
<div className="card">
<div className="d-flex justify-content-between align-items-center">
<div className="d-flex flex-row align-items-center time">
<i className=""></i>
<small className="ml-1">{product.vendorName}</small>
</div>
</div>
<div className="text-center">
<img src="https://i.imgur.com/TbtwkyW.jpg" width="250" />
</div>
<div className="text-center">
<h5>{product.prodName}</h5>
<span className="text-success">${product.price}</span>
</div>
<div>
<Link to={`/product/${product.id}`}>
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
>
<i
className="bi-bag-fill me-1"
style={{ marginRight: "4px" }}
></i>
Buy Now
</button>
</Link>
<Link to={`/product/${product.id}`}>
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
>
<i className=""></i>
View
</button>
</Link>
</div>
</div>
</div>
))}
{/* <img src="https://i.imgur.com/aTqSahW.jpg" width="250" /> */}
</div>
</div>
</>
);
}
}
export default ProductGridComponent;
ProductViewComponent.jsx
class ProductViewComponent extends React.Component {
constructor(props) {
super(props);
const windowUrl = window.location.pathname.substring(1);
console.log("window url for product: ", windowUrl);
this.state = {
//id: this.props.match.params.id,
id: windowUrl.substring(windowUrl.indexOf("/") + 1), //Please fix me, I am vulnerable to SQL Injection
name: "",
price: 0,
vendor: "holder vendor",
description: "",
};
console.log("ID: ", this.state.id);
}
componentDidMount() {
ProductService.getProductById(this.state.id).then((res) => {
let product = res.data;
this.setState({
name: product.prodName,
price: product.price,
vendor: product.vendorName,
description: product.description,
});
});
}
render() {
return (
...
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
onClick={() =>
this.props.onAddToCart(
this.state.id,
this.state.name,
this.state.description,
this.state.price
)
}
>
<i className="bi-cart-fill me-1"></i>
Add to cart
</button>
...
I was struggling with a similar problem earlier today. My localStorage was getting rewritten upon refresh even though localStorage is often used to carry data over between refreshes / browser closures.
The problem that I realized was that I was setting the localStorage to the state upon render, and my state was initialized to an empty value. It looks like you are only calling localStorage.setItem() once in your code, and it is setting it to the state. The problem is that when localStorage.setItem() is called, your state is still an empty array.
React's this.setState() method is an asynchronous method, meaning that it will be added to a stack to be run. It gives no promises on when it will start being run or when it will finish. It looks like you are calling this.setState() right before you call localStorage.setItem(), which means that it is not updating the state in time before you are changing the localStorage.
What I would suggest is putting the call to localStorage inside of the callback function that is the second parameter of this.setState(). This callback function is always run after the state has been set.
Your state setter will look like this:
this.setState(
{
cart: [...this.state.cart, holder],
},
() => {
console.log("Updated Cart: ", this.state.cart);
localStorage.setItem("cart", JSON.stringify(this.state.cart));
}
);
The issue was a logical error. On reload, the state(cart) becomes empty. However, I never set the state to the items already on local storage before trying add the new items to the cart.
The way I did the adding functionality involves taking the current state and appending the new items, and then setting the local storage afterwards. Meaning that if the state is empty then then trying to add a new item this way will simply result in only the new item being on the cart.
I added this piece to App.js to solve the error.
componentDidMount() {
const sCart = localStorage.getItem("cart");
const parsedCart = JSON.parse(sCart);
if (sCart == null) {
this.setState({ cart: [] });
} else {
console.log("cart String mount on shopping cart: ", sCart);
console.log("cart Object at mount on shopping cart: ", parsedCart);
this.setState(
{
cart: parsedCart,
}
);
}
}

A modal inside a Map Function In ReactJs has error message

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;

How can I display array of data in specific div id by click in a button?

I create a component of react. and there one array with some values. so I need to display that value or data in any specific div by clicking in a button.
this is my array in component.
constructor(){
super()
this.state = {
notificaion: [
"Notification-1",
"Notification-3",
"Notification-4",
"Notification-5",
]
}
}
this is my button with click event.
<button onClick={this.getNotification}>{this.state.notificaion.length}</button>
this is the function that I have create. and to push data in specific div.
getNotification = () =>{
return(
this.state.notificaion.map(items =>(
<li key={items}>{items}</li>
))
)
}
here I want to display when buttons is clicked
<strong>{this.getNotification()}</strong>
This is my full code that I have been tried.
import React, {Component} from 'react';
class Menu2 extends Component{
constructor(){
super()
this.state = {
notificaion: [
"Notification-1",
"Notification-3",
"Notification-4",
"Notification-5",
]
}
}
getNotification = () =>{
return(
this.state.notificaion.map(items =>(
<li key={items}>{items}</li>
))
)
}
render(){
return(
<div className="header">
<div className="container">
<div className="row">
<div className="col-lg-12 col-sm-12 col-xs-12">
<div className="text-center mb-20">
<h1>Notificaion Status</h1>
<p>Check notificatin read/unread</p>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12 col-sm-12 col-xs-12">
<div className="card border-dark mb-3">
<div className="card-body text-dark">
<p className="card-text" style={{textAlign: 'center'}}>
{this.state.notificaion.length > 0
?
<span>You Have <button onClick={this.getNotification}>{this.state.notificaion.length}</button> Unread Notifications</span>
:
<span>You Have <button onClick={this.getNotification}>{this.state.notificaion.length}</button> Unread Notifications}</span>}
</p>
<strong>{this.getNotification()}</strong>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Menu2;
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
notificaion: [
"Notification-1",
"Notification-3",
"Notification-4",
"Notification-5",
],
notificationHtml: ""
}
}
getNotification = () => {
this.setState({
notificationHtml: this.state.notificaion.map(items => (
<li key={items}>{items}</li>
))
});
}
render() {
return (
<div className="App">
<button onClick={this.getNotification}>{this.state.notificaion.length}</button>
<div>
{this.state.notificationHtml}
</div>
</div>
);
}
}
export default App;
I would implemented as such:
this.state = {
visible: false,
notifications: ...
}
toggleVisibility() =>{
this.setState({
visibile: true
})
}
Don't forget to bind the "toggleVisibility" function. Then
in your component:
<button onClick={this.toggleVisibility}/>
...
{if(this.state.visible){
<strong>this.state.notifications.map(notification,i) =>
<li key={i}>{notification}</li>
</strong>
}
You can add a property showNotification in state. And based on the value of it, we can show the notification.
Also add a method showNotificationHandler that toggles the showNotification value.
class Menu2 extends Component {
constructor() {
super();
this.state = {
notificaion: [
"Notification-1",
"Notification-3",
"Notification-4",
"Notification-5"
],
// adding a property "showNotification"
showNotification: false
};
}
getNotification = () => {
return this.state.notificaion.map(items => <li key={items}>{items}</li>);
};
// method that toggles the "showNotification" value
showNotificationHandler = () => {
this.setState(({ showNotification }) => ({
showNotification: !showNotification
}));
};
render() {
return (
<div className="header">
<div className="container">
<div className="row">
<div className="col-lg-12 col-sm-12 col-xs-12">
<div className="text-center mb-20">
<h1>Notificaion Status</h1>
<p>Check notificatin read/unread</p>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12 col-sm-12 col-xs-12">
<div className="card border-dark mb-3">
<div className="card-body text-dark">
<p className="card-text" style={{ textAlign: "center" }}>
{this.state.notificaion.length > 0 ? (
<span>
You Have{" "}
<button onClick={this.showNotificationHandler}>
{this.state.notificaion.length}
</button>{" "}
Unread Notifications
</span>
) : (
<span>
You Have{" "}
<button onClick={this.showNotificationHandler}>
{this.state.notificaion.length}
</button>{" "}
Unread Notifications}
</span>
)}
</p>
<strong>
// Depending on the value of "showNotification" we get notification
// if "showNotification" is true then get the notification
{this.state.showNotification && this.getNotification()}
</strong>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Menu2;

How do I validate textfields in a dynamic array

I have one mother component and one child component. In the child component there are two textfields (name and email) and an add button. When the user presses on add a new set of the same textfields will be rendered. I save those textfields in my mother component in an array. I want to have a validate function that checks of the values are valid. If not i want to give the error props a true value so that the user can see that field is wrong. The only problem i have is figuring out how to give right textfield in the array the error value. Because now every textfield will get the error value.
Child Component:
import React from 'react';
import TextField from '#material-ui/core/TextField';
import AddIcon from '#material-ui/icons/Add';
import Minus from '#material-ui/icons/Delete';
import Button from '#material-ui/core/Button';
class People extends React.Component {
constructor(props){
super(props);
}
render(){
const {classes} = this.props;
return (
<div>
{this.props.people.map((person, index) => (
<div key={person}>
<div className="container">
<div className="row">
<div className="col s2">
<Button
mini
variant="fab"
color="primary"
aria-label="minus"
onClick={e =>
this.props.removeRow(index)}
data-toggle="tooltip"
>
<Minus/>
</Button>
</div>
<div>
<div className="col s5">
<TextField
name="name"
id="standard-dense"
label="Teamlid naam"
margin="dense"
value={person.name}
error={e =>
this.props.validate(e, index)}
onChange={e =>
this.props.onChange(e, index)}
/>
</div>
<div className="col s5">
<TextField
name="email"
id="standard-dense"
label="Teamlid email"
margin="dense"
value={person.email}
error={e =>
this.props.validate(e, index)}
onChange={e =>
this.props.onChange(e, index)}
/>
</div>
</div>
</div>
</div>
</div>
))}
<div className="container">
<div className="row">
<div className="col s2">
<Button
mini
variant="fab"
color="primary"
aria-label="Add"
onClick={this.props.addRow}
data-toggle="tooltip"
className="btn btn-xs btn-primary"
data-original-title="">
<AddIcon/>
</Button>
</div>
<div className="col s5">
</div>
<div className="col s5">
</div>
</div>
</div>
</div>
);
}
};
export default People;
Mother Component:
import React, {Component} from 'react';
import People from './People';
class Form extends React.Component{
constructor(props){
super(props);
this.state = {
people: []
}
}
addRow = () => {
this.setState(previousState => {
return {
people: [...previousState.people, {name: "", email: "", error:false}]
};
});
};
removeRow = index => {
this.setState(previousState => {
const people = [...previousState.people];
people.splice(index, 1);
return { people };
});
};
//onChange functie van de teamleden rijen
onChange = (event, index) => {
const { name, value } = event.target;
this.setState(previousState => {
const people = [...previousState.people];
people[index] = { ...people[index], [name]: value };
return { people };
});
};
validate = (event,index) => {
event.preventDefault();
if(!event.target.value){
return true;
}else{
return false;
}
};
render(){
const {classes} = this.props;
return(
<form>
<People
addRow={this.addRow}
removeRow={this.removeRow}
onChange={this.onChange}
people={this.state.people}
validate={this.validate}
/>
<button onClick={this.validate}>Validate</button>
</form>
);
}
}
export default Form;
I know that the validate function probably needs the index of the wrong textfield. But I don't know how to properly implement it.

Pass in states as props to another component in iteration

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.

Categories

Resources