onSubmit method executing on render in React with Firebase - javascript

I am trying to set a document inside a collection in Firebase FireStore.
I created a component that render a div with a button inside and then it conditionally renders a FormValidation (a form) from open state (a module for easy form validation in react).
The button is setting open to true if it is not already true.
Then the formvalidation is rendered, it has an onsubmit method, a button with type="submit" and a button that sets open to false if it isn't already false (so the formvalidation dissapears).
So far so good, everything should be working fine but for some reason, whenever I click the button to show the formvalition the onSubmit runs.
Obviosly it shouldn't run until you hit the button with type="submit"
Here's the full code:
import React from 'react'; import { ValidatorForm } from 'react-form-validator-core'; import TextInput from '../TextInput'; import { compose } from 'recompose';
import PlusIcon from '../../resources/icons/add.svg'; import PlusIconBlack from '../../resources/icons/addBlack.svg'; import ArrowIcon from '../../resources/icons/arrowDown.svg'; import BinIcon from '../../resources/icons/rubbishBin.svg';
import ButtonStyles from './button.module.css'; import ListStyles from './list.module.css'; import GameStyles from './game.module.css';
import { withAuthorization } from '../Session'; import { withFirebase } from '../Firebase';
class Games extends React.Component { constructor(props) {
super(props); }
render() {
return(
<div style={{marginBottom: '6em'}}>
<h1 style={{marginTop: '2em'}}>Listas de Videojuegos</h1>
<span style={{display: 'block', width: '100%', height: '2px', background: '#ccc', marginTop: '0.5em'}} />
<ListsContainer />
</div>
); } }
class ListsContainer extends React.Component { constructor(props) {
super(props); }
render() {
return(
<div style={{marginTop: '3em'}}>
<AddButton backgroundColor="#8489C8" icon={PlusIcon} text="Añadir lista" textColor="#fff" type="list" />
<List img="https://i.pinimg.com/originals/30/0e/58/300e58c8416a68dcfcf1761501348243.jpg" backgroundColor="#6168B8" name="Lista 1" games="5" />
<List img="https://i.pinimg.com/originals/30/0e/58/300e58c8416a68dcfcf1761501348243.jpg" backgroundColor="#6168B8" name="Lista 2" games="26" />
<List img="https://i.pinimg.com/originals/30/0e/58/300e58c8416a68dcfcf1761501348243.jpg" backgroundColor="#6168B8" name="Lista 1" games="5" />
</div>
); } }
class List extends React.Component { constructor(props) {
super(props);
this.state = {
open: false,
};
this.openGames = this.openGames.bind(this); }
openGames() {
if(this.state.open === true) {
this.setState({
open: false
});
} else {
this.setState({
open: true
});
} }
render() {
const open = this.state.open;
return(
<div class={ListStyles.list} style={{background: this.props.backgroundColor}}>
<img className={ListStyles.img} src={this.props.img} />
<div className={ListStyles.textBox}>
<h2>{this.props.name}</h2>
<span><b>{this.props.games}</b> Juegos</span>
</div>
<button className={open ? ListStyles.collapseUp : ListStyles.collapse} onClick={this.openGames}><img src={ArrowIcon} alt="" /></button>
<button className={ListStyles.bin} onClick={this.openGames}><img src={BinIcon} alt="" /></button>
<div style={{marginTop: '3em'}} className={open ? ListStyles.games : ListStyles.gamesNotOpen}>
<Game name="League Of Legends" platform="PC" hours="2000" />
<Game name="Borderlands" platform="XBOX 360" hours="50" />
<span style={{display: 'block', height: '1em'}} />
<AddButton backgroundColor="#fff" icon={PlusIconBlack} text="Añadir juego" textColor="#363636" type="game" />
</div>
</div>
); } }
class Game extends React.Component { constructor(props) {
super(props); }
render() {
return(
<div class={GameStyles.gameBox}>
<h3>{this.props.name}</h3>
<span><b>{this.props.hours}</b> Horas jugadas</span>
<span><b>{this.props.platform}</b></span>
<button className={GameStyles.bin} ><img src={BinIcon} alt="" /></button>
</div>
); } }
class AddButtonBase extends React.Component { constructor(props) {
super(props);
this.state = {
open: false,
listName: '',
imgUrl: '',
gameName: '',
platform: '',
hours: ''
};
this.openForm = this.openForm.bind(this);
this.closeForm = this.closeForm.bind(this);
this.openFormList = this.openFormList.bind(this);
this.openFormGame = this.openFormGame.bind(this); }
submitList = (authUser) => {
console.log(authUser)
const { listName, imgUrl } = this.state;
this.props.firebase.list(listName, JSON.parse(authUser).uid).set(
{
imgUrl,
},
{ merge: true },
).then(() => this.closeList())
.catch(error => console.log(error.message)); }
submitGame = event => {
//const { gameName, platform, hours } = this.state;
console.log("SASNJKAB") }
handleChangeList = event => {
this.setState({ [event.target.name]: event.target.value }); };
handleChangeGame = event => {
this.setState({ [event.target.name]: event.target.value }); };
openForm() {
if(this.state.open === false) {
this.setState({
open: true
});
} }
closeForm() {
if(this.state.open === true) {
this.setState({
open: false
});
} }
openFormList = () => (
<ValidatorForm className={ButtonStyles.formList} ref="loginForm" onSubmit={this.submitList(localStorage.getItem("authUser"))}>
<h3>Añadir Lista</h3>
<span style={{display: 'block', width: '100%', height: '2px', background: '#fff', marginTop: '0.5em', marginBottom: '1.5em'}} />
<TextInput
style={{width: '26em'}}
type="text"
name="listName"
title="Nombre de la lista"
onChange={this.handleChangeList}
value={this.state.listName}
validators={['required', 'maxStringLength:20']}
errorMessages={['Campo obligatorio', 'Se ha excedido el límite de caracteres']} /><br />
<TextInput
style={{width: '26em'}}
type="text"
name="imgUrl"
title="Url para el icono de la lista"
onChange={this.handleChangeList}
value={this.state.imgUrl}
validators={['required']}
errorMessages={['Campo obligatorio']} /><br />
<div style={{textAlign: 'right'}}>
<button type="submit" className={ButtonStyles.createList}>Crear nueva lista</button>
<button className={ButtonStyles.closeList} onClick={this.closeForm}>Cerrar</button>
</div>
</ValidatorForm> )
openFormGame = () => (
<ValidatorForm className={ButtonStyles.formGame} ref="loginForm" onSubmit={this.submitGame()}>
<h3>Añadir Juego</h3>
<span style={{display: 'block', width: '100%', height: '2px', background: '#fff', marginTop: '0.5em', marginBottom: '1.5em'}} />
<TextInput
style={{width: '26em'}}
type="text"
name="gameName"
title="Nombre del videojuego"
onChange={this.handleChangeList}
value={this.state.gameName}
validators={['required', 'maxStringLength:20']}
errorMessages={['Campo obligatorio', 'Se ha excedido el límite de caracteres']} /><br />
<TextInput
style={{width: '26em'}}
type="text"
name="platform"
title="Plataforma"
onChange={this.handleChangeList}
value={this.state.platform}
validators={['required', 'maxStringLength:10']}
errorMessages={['Campo obligatorio', 'Se ha excedido el límite de caracteres']} /><br />
<TextInput
style={{width: '26em'}}
type="text"
name="hours"
title="Horas jugadas"
onChange={this.handleChangeList}
value={this.state.hours}
validators={['required', 'isNumber']}
errorMessages={['Campo obligatorio', 'Este campo sólo admite números']} /><br />
<div style={{textAlign: 'right'}}>
<button type="submit" className={ButtonStyles.createGame}>Crear nueva lista</button>
<button className={ButtonStyles.closeGame} onClick={this.closeForm}>Cerrar</button>
</div>
</ValidatorForm> )
render() {
return(
<div>
<button onClick={this.openForm} className={ButtonStyles.button} style={{ background: this.props.backgroundColor, color: this.props.textColor }}><img src={this.props.icon} /> <span>{this.props.text}</span></button>
{
(this.state.open && (this.props.type === "list")) ? this.openFormList() : null
}
{
this.state.open && this.props.type === "game" ? this.openFormGame() : null
}
</div>
); } }
const AddButton = withFirebase(AddButtonBase);
const condition = authUser => !!authUser;
export default withAuthorization(condition)(Games);

I believe your issue is that you are calling your submission methods when you are assigning them as callbacks.
You are calling this method within the brackets:
onSubmit={this.submitList(localStorage.getItem("authUser"))}
Here as well:
onSubmit={this.submitGame()}
Compare with your onclick handlers:
onClick={this.closeForm}
There are a few ways you can fix this, such as by removing the calling parentheses, or by wrapping them in anonymous functions/closures, for example:
onSubmit={()=> this.submitList(localStorage.getItem("authUser"))}

Related

How to open child component modal from parent by using its key in ReactJs

I am creating my blog where I want to edit the each blog respectively on click of it edit button.
Now modal is opening for respective id but value is not set with state value.
That is componentwillmount is not properly working.
Any suggestion what i made mistake here ? It will be helpful.
How can I make this possible witht he current set of code.
//blog.js (Parent component)
import React, {Component} from 'react';
import ReactGA from 'react-ga';
import {Card, Grid, Cell, Dialog, CardMenu, Button, CardTitle, CardText, CardActions, FABButton, Icon} from'react-mdl';
import { Container, Modal, ModalHeader, ModalBody, Form, FormGroup, Label, Input,} from 'reactstrap';
import { connect } from 'react-redux';
import { getBlog, deleteBlog, updateBlog } from '../../actions/resumeActions';
import PropTypes from 'prop-types';
import Loading from './Loading';
import Moment from 'moment';
import BlogModal from "./BlogModal";
import Pagination from "react-js-pagination";
// import EditBlog from "./EditBlog";
class Blogs extends Component{
initializeReactGA() {
ReactGA.initialize('UA-132348738-1');
ReactGA.pageview('/contact');
}
constructor(props) {
super(props);
this.state = {
modal: false,
justClicked: null,
activePage: 1,
requiredItem : null,
_id: '',
blog_short_desc: '',
blog_name: '',
blog_desc: '',
blog_image_link: '',
blog_by: '',
blog_by_author: ''
};
this.handleOpenDialog = this.handleOpenDialog.bind(this);
this.handleCloseDialog = this.handleCloseDialog.bind(this);
this.replaceModalItem = this.replaceModalItem.bind(this);
this.onTodoChange = this.onTodoChange.bind(this);
}
static propTypes = {
getBlog: PropTypes.func.isRequired,
deleteBlog: PropTypes.func.isRequired,
updateBlog: PropTypes.func.isRequired,
resume: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
loading: PropTypes.object.isRequired
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
value: nextProps.name
})
}
toggle = (id) => {
this.setState({
modal: !this.state.modal
});
}
componentWillMount() {
debugger
this.props.resume["blogs"].map((blog) => {
this.setState({
updatable : false,
_id: blog._id,
blog_short_desc: blog.blog_short_desc,
blog_name: blog.blog_name,
blog_desc: blog.blog_desc,
blog_image_link: blog.blog_image_link,
blog_by: blog.blog_by,
blog_by_author: blog.blog_by_author
});
})
}
replaceModalItem(id) {
debugger
this.setState({
modal: true,
requiredItem: id
});
debugger
}
onTodoChange = (e) => {
this.setState({
[e.target.name] : e.target.value
});
}
onSubmit = (e, id) => {
e.preventDefault();
const updatedBlog = {
blog_short_desc: this.state.blog_short_desc,
blog_name: this.state.blog_name,
blog_desc: this.state.blog_desc,
blog_image_link: this.state.blog_image_link,
blog_by: this.props.auth["user"]._id,
blog_by_author: this.props.auth["user"].name
}
//update blog via updateblog action
this.props.updateBlog(id, updatedBlog, this.props.history);
alert("Blog updated successfully!");
//close modal
e.target.reset();
this.toggle();
}
handleOpenDialog(id) {
this.setState({
openDialog: true,
OpenEditDialog: true,
justClicked: id
});
}
handleCloseDialog() {
this.setState({
openDialog: false
});
}
componentDidMount() {
this.props.getBlog();
}
onDeleteBlogClick = (id) => {
this.props.deleteBlog(id);
};
handlePageChange(pageNumber) {
this.setState({activePage: pageNumber});
}
cardDialog(blogs, user){
const itemsPerPage = 6;
let activeBlogs = blogs.slice (itemsPerPage * this.state.activePage - itemsPerPage, itemsPerPage * this.state.activePage);
return(
<Grid style={{padding: 0, display: 'contents'}}>
{activeBlogs.map(({ _id, blog_name, blog_desc, blog_image_link, blog_by_author }) => (
<Cell col={12}>
<Dialog open={this.state.openDialog && this.state.justClicked === _id} className="open-dialog">
{blog_image_link ?
(<CardTitle style={{color: '#fff', height: '176px', backgroundImage: `url(${blog_image_link})`, backgroundPosition: 'center',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat'}}>{blog_name}</CardTitle>) :
(<CardTitle className="card-blog-title-image">{blog_name}</CardTitle>
)
}
<CardText>
{blog_desc}
</CardText>
<CardActions border>
<p style={{float:'right', fontWeight:'bold'}}>Author: {blog_by_author}</p>
</CardActions>
<CardMenu style={{color: '#fff'}}>
<FABButton onClick={this.handleCloseDialog} className="close-button" >
<Icon name="close" />
</FABButton>
</CardMenu>
</Dialog>
</Cell>
))}
</Grid>
)
}
editcardDialog(blogs, user){
const itemsPerPage = 6;
let activeBlogs = blogs.slice (itemsPerPage * this.state.activePage - itemsPerPage, itemsPerPage * this.state.activePage);
return(
<span>
<a className="btn edit-btn-blog-post" href="#" onClick={this.toggle} title="Edit Blog">
<i className="fa fa-pencil" aria-hidden="true"></i>
</a>
{activeBlogs.map(({ _id, blog_short_desc, blog_name, blog_desc, blog_image_link, blog_by_author }) => (
<Modal
isOpen = {this.state.modal && this.state.requiredItem === _id}
toggle = {()=>this.toggle(_id)}
>
<ModalHeader toggle={this.toggle} style={{fontWeight: "bold"}}>
Edit your blog {this.state.blog_name}
</ModalHeader>
<ModalBody>
<Form onSubmit={e => this.onSubmit(e, this.state._id )}>
<FormGroup>
<Label for="blogHeading">Blog Heading</Label>
<Input type="text" name="blog_short_desc" id="blogHeading" placeholder="Update one liner"
onChange={this.onTodoChange} value={blog_short_desc}/>
<Label for="blogName">Blog Name</Label>
<Input type="text" name="blog_name" id="blogName" placeholder="Update blog name"
onChange={this.onTodoChange} value={blog_name}/>
<Label for="desc1">Description </Label>
<Input type="textarea" name="blog_desc" id="desc1" placeholder="Update your blog"
onChange={this.onTodoChange} value={blog_desc}/>
<Label for="imageUrl">Image Url</Label>
<Input type="text" name="blog_image_link" id="imageUrl" placeholder="Update image url (Optional)"
onChange={this.onTodoChange} value={blog_image_link}/>
<Button
color="dark"
style={{marginTop: '2rem'}}
block
>Edit blog</Button>
</FormGroup>
</Form>
</ModalBody>
</Modal>
))}
</span>
)
}
render(){
const { blogs, loading} = this.props.resume;
const { user, isAuthenticated } = this.props.auth;
const itemsPerPage = 6;
let activeBlogs = blogs.slice (itemsPerPage * this.state.activePage - itemsPerPage, itemsPerPage * this.state.activePage);
return(
<Container>
{loading ? (
<div><Loading/></div>
) : (
<div>
{/* blog modal */}
<BlogModal />
{/* card dialog */}
{this.cardDialog(blogs, user)}
{this.editcardDialog(blogs, user)}
<Grid style={{padding: 0}} id="todo">
{activeBlogs.map((item, i) => (
<Cell key={item._id} data-id={item._id}>
<Card shadow={5} className="cards-grid">
{item.blog_image_link ?
(<CardTitle style={{color: '#fff', height: '200px',
width: 'auto', backgroundImage: `url(${item.blog_image_link})`, backgroundPosition: 'center',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat'}}></CardTitle>) :
(<CardTitle className="card-title-image"></CardTitle>
)
}
<CardText>
<b>{item.blog_short_desc}</b>
</CardText>
<CardActions border>
<p>
<Button className="blog-read-me-button col-4" onClick={this.handleOpenDialog.bind(this, item._id)}>Read </Button>
{ isAuthenticated && (item.blog_by === user._id) ?
<span className="col=8">
<Button className="remove-btn-blog-post"
color="danger"
size="sm"
onClick= {this.onDeleteBlogClick.bind(this, item._id)} title="Delete Blog">
×
</Button>
<a className="btn edit-btn-blog-post" href="#" onClick={this.replaceModalItem.bind(this, item._id)} title="Edit Blog">
<i className="fa fa-pencil" aria-hidden="true"></i>
</a>
{/* <a className="btn edit-btn-blog-post" href="#" onClick={this.handleEditOpenDialog.bind(this, item._id)} title="Edit Blog">
<i className="fa fa-pencil" aria-hidden="true"></i>
</a> */}
</span> : null }
</p>
<p style={{ fontStyle:'italic', fontWeight:'bold'}}>By-{item.blog_by_author} <span style={{float:'right',}}>{Moment(item.date).format('Do MMMM YYYY')}</span></p>
</CardActions>
</Card>
</Cell>
))}
</Grid>
</div>
)}
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={6}
totalItemsCount={blogs.length}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
itemClass='page-item'
linkClass='page-link'
/>
</Container>
)
}
}
const mapStateToProps = (state) => ({
resume: state.resume,
auth: state.auth,
loading: state.apiCallsInProgress > 0
});
export default connect(mapStateToProps, {getBlog, deleteBlog, updateBlog }) (Blogs);
Why don't you handle all your posts with your parent component, and use the <EditBlog / only as a functinal component. Here is a example and the correponding fiddle:
const EditBlog = ({title, content, handleEdit}) => {
return (
<div classname="editblog">
<h1>{title}</h1>
<p>{content}</p>
<button onClick={handleEdit}>Edit</button>
</div>
)
}
class Blog extends React.Component {
constructor(props) {
super(props);
this.state= {
activeBlogs: [
{title: "Heading 1", content: "Content 1"},
{title: "Heading 2", content: "Content 2"}
],
editId: -1,
}
this.handleEdit = this.handleEdit.bind(this);
}
handleEdit(e) {
const {editId, activeBlogs} = this.state;
let newActiveBlogs = [...activeBlogs];
const {name, value } = e.target;
newActiveBlogs[editId]= Object.assign(newActiveBlogs[editId], {[name]: value});
if(editId >= 0) {
this.setState({
activeBlogs: newActiveBlogs
})
}
}
render() {
const {activeBlogs, editId} = this.state;
return (
<div classname="blog">
{activeBlogs.map((item, index) => {
return <EditBlog key={index} {...item} handleEdit={()=>this.setState({editId: index})} />
})}
{editId >= 0 && activeBlogs.length &&
<div classname="modal">
<h2>Edit Modal is open</h2>
<input name="title" value={activeBlogs[editId].title} onChange={this.handleEdit}></input>
<input name="content" value={activeBlogs[editId].content} onChange={this.handleEdit}></input>
<button onClick={()=>this.setState({editId:-1})}>Save</button>
</div>
}
</div>
)
}
}
ReactDOM.render(
<Blog name="Blog" />,
document.getElementById('container')
);
fiddle
Try the below one
class EditBlog extends Component {
constructor(props) {
super(props);
this.state = {
modal: null,
requiredItem : null,
_id: '',
blog_short_desc: '',
blog_name: '',
blog_desc: '',
blog_image_link: '',
blog_by: '',
blog_by_author: ''
};
this.replaceModalItem = this.replaceModalItem.bind(this);
this.onTodoChange = this.onTodoChange.bind(this);
}
static propTypes = {
auth: PropTypes.object.isRequired,
updateBlog: PropTypes.func.isRequired,
editBlog: PropTypes.func.isRequired,
resume: PropTypes.object.isRequired,
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
value: nextProps.name
})
}
toggle = (id) => {
this.setState({
modal: id
});
}
componentWillMount() {
this.props.resume["blogs"].map((blog) => {
this.setState({
updatable : false,
_id: blog._id,
blog_short_desc: blog.blog_short_desc,
blog_name: blog.blog_name,
blog_desc: blog.blog_desc,
blog_image_link: blog.blog_image_link,
blog_by: blog.blog_by,
blog_by_author: blog.blog_by_author
});
})
}
replaceModalItem(id) {
this.setState({
openDialog: true,
OpenEditDialog: true,
requiredItem: id
});
}
onTodoChange = (e) => {
this.setState({
[e.target.name] : e.target.value
});
}
onSubmit = (e, id) => {
e.preventDefault();
const updatedBlog = {
blog_short_desc: this.state.blog_short_desc,
blog_name: this.state.blog_name,
blog_desc: this.state.blog_desc,
blog_image_link: this.state.blog_image_link,
blog_by: this.props.auth["user"]._id,
blog_by_author: this.props.auth["user"].name
}
//update blog via updateblog action
this.props.updateBlog(id, updatedBlog, this.props.history);
alert("Blog updated successfully!");
//close modal
e.target.reset();
this.toggle();
}
render(){
return(
<span>
<a className="btn edit-btn-blog-post" href="#" onClick={()=>this.toggle(this.state._id)} title="Edit Blog">
<i className="fa fa-pencil" aria-hidden="true"></i>
</a>
<Modal
isOpen = {this.state.modal===this.state._id}
toggle = {()=>this.toggle(this.state._id)}
>
<ModalHeader toggle={()=>this.toggle(this.state._id)} style={{fontWeight: "bold"}}>
Edit your blog {this.state.blog_name}
</ModalHeader>
<ModalBody>
<Form onSubmit={e => this.onSubmit(e, this.state._id, )}>
<FormGroup>
<Label for="blogHeading">Blog Heading</Label>
<Input type="text" name="blog_short_desc" id="blogHeading" placeholder="Update one liner"
onChange={this.onTodoChange} value={this.state.blog_short_desc}/>
<Label for="blogName">Blog Name</Label>
<Input type="text" name="blog_name" id="blogName" placeholder="Update blog name"
onChange={this.onTodoChange} value={this.state.blog_name}/>
<Label for="desc1">Description </Label>
<Input type="textarea" name="blog_desc" id="desc1" placeholder="Update your blog"
onChange={this.onTodoChange} value={this.state.blog_desc}/>
<Label for="imageUrl">Image Url</Label>
<Input type="text" name="blog_image_link" id="imageUrl" placeholder="Update image url (Optional)"
onChange={this.onTodoChange} value={this.state.blog_image_link}/>
<Button
color="dark"
style={{marginTop: '2rem'}}
block
>Edit blog</Button>
</FormGroup>
</Form>
</ModalBody>
</Modal>
</span>
)
}
}
const mapStateToProps = state => ({
resume: state.resume,
auth: state.auth
})
export default connect(mapStateToProps, { updateBlog })(EditBlog);

react-carousel-slider doesn't rerender

guys! Have a problem with rerendering of slider component. After choosing another SELECT option, other images are to be loaded to carousel component. But!! Nothing happens! Props of component are being changed, and developer tools show slides (images) are changed, but nothing happens on DOM.
Below i post code. What do you think? Where is the problem?
import React from 'react';
import CarouselSlider from "react-carousel-slider";
import { FormControl } from 'react-bootstrap';
class StampChoose extends React.Component {
changeSamplesType = (e) => {
const sampleType = e.target.value;
this.props.changeSamplesType(sampleType);
this.forceUpdate();
}
render() {
let btnWrapperStyle = {
position: "relative",
borderRadius: "50%",
height: "50px",
width: "50px",
textAlign: "center"
}
let btnStyle = {
display: "inline-block",
position: "relative",
top: "90%",
transform: "translateY(-50%)",
fontSize: "36px"
}
let rBtnCpnt = (<div style = {btnWrapperStyle} >
<div style = {btnStyle} className = "material-icons" >
<i className="fas fa-angle-right"></i>
</div>
</div>);
let lBtnCpnt = (<div style = {btnWrapperStyle} >
<div style = {btnStyle} className = "material-icons" >
<i className="fas fa-angle-left"></i>
</div>
</div>);
let iconItemsStyle = {
padding: "0px",
background: "transparent",
margin:"0 5px",
height: "80%"
};
const titles = this.props.titles;
const slides = this.props.slides;
return (
<React.Fragment>
<FormControl componentClass="select" onChange={ this.changeSamplesType }>
<option value="type1">{ titles['type1'] }</option>
<option value="type2">{ titles['type2'] }</option>
<option value="type3">{ titles['type3'] }</option>
<option value="type4">{ titles['type4'] }</option>
</FormControl>
<CarouselSlider
sliderBoxStyle = {{height: "150px", width: "90%", background: "transparent", overflow: "hidden"}}
accEle = {{dots: false}}
newState={ this.state }
slideCpnts = {slides}
itemsStyle = {iconItemsStyle}
buttonSetting = {{placeOn: 'middle-outside'}}
rBtnCpnt = {rBtnCpnt}
lBtnCpnt = {lBtnCpnt}
/>
</React.Fragment>
)
}
}
export default StampChoose;
import React from 'react';
import { Grid, Row, Col, ControlLabel } from 'react-bootstrap';
import { samples, titles} from '../../../samples-stamps';
import StampChoose from './StampChoose';
const Sample = (props) => (
<React.Fragment>
{
<div>
<img src={ `images/samples/${props.img}` } alt={ props.title } />
</div>
}
</React.Fragment>
);
class StampsSamples extends React.Component {
state = {
sampleType: 'type1'
}
changeSamplesType = (sampleType) => {
this.setState({ sampleType });
}
render() {
const sampleType = this.state.sampleType;
let slides = Object.keys(samples[sampleType]).map((item, i) => {
return (
<div>
<Sample
key={i}
title={ samples[sampleType][item].title }
img={ samples[sampleType][item].img }
productId={ samples[sampleType][item].id }
/>
</div>
);
});
return (
<Grid>
<Row>
<Col md={ 4 }>
<ControlLabel>Примерный образец оттиска <br/>
<small>(выберите образец оттиска)</small>
</ControlLabel>
</Col>
<Col md={ 8 }>
<StampChoose
slides={ slides }
titles={ titles }
changeSamplesType={ this.changeSamplesType }
/>
</Col>
</Row>
</Grid>
);
}
}
export default StampsSamples;
In your Sample Component your returning an object inside of React.Fragment. Does it have anything to do with that? What if you remove the { and } inside there and try? Like below. Don't know if thats the issue but try. You also have an extra DIV in your map method for the slides. If you check the instructions for the React Carousel Slider they dont use these extra DIVs and {}
<React.Fragment>
<div>
<img src={ `images/samples/${props.img}` } alt={ props.title } />
</div>
</React.Fragment>

Passing Props to Child component from Parent using cloneElement

In the code below when the checkbox is checked in AddressWrapper the Ship To input in the AddressForm should be disabled. I can not figure out why AddressWrapper cloneElement is not passing it's state to the child. I have checked out many links about this issue and as far as I can tell this should work. This is the closest How to pass props to {this.props.children} to this problem but it is using a callback from the child to the parent and I need a change in parent state to update the child. I could use a publish/subscribe to do it but I'm trying to do it the 'React' way.
class AddressForm extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "Joyce",
disableInputs: props.billToSameAsShipTo
};
this.handleBillToSameAsShipToChanged = this.handleBillToSameAsShipToChanged.bind(
this
);
}
handleBillToSameAsShipToChanged() {
this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
}
handleFirstNameChanged(ev) {
this.setState({ firstName: ev.target.value });
}
render() {
return (
<form>
<div className="form-row">
<div className="col-6">
<input
type="text"
className="form-control"
placeholder="First name"
disabled={this.state.disableInputs}
value={this.state.firstName}
onChange={this.handleFirstNameChanged.bind(this)}
/>
</div>
</div>
</form>
);
}
}
class AddressFormWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
billToSameAsShipTo: true
};
this.handlebillToSameAsShipToChanged = this.handlebillToSameAsShipToChanged.bind(
this
);
}
handlebillToSameAsShipToChanged() {
this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
}
render() {
const billToSameAsShipTo = () => {
if (this.props.showSameAsShipTo === true) {
return (
<span style={{ fontSize: "10pt", marginLeft: "20px" }}>
<input
type="checkbox"
checked={this.state.billToSameAsShipTo}
onChange={this.handlebillToSameAsShipToChanged}
/>
<span>Same as Ship To</span>
</span>
);
}
};
const childWithProp = React.Children.map(this.props.children, child => {
return React.cloneElement(child, { ...this.state });
});
return (
<span className="col-6">
<h3>
{this.props.title}
{billToSameAsShipTo()}
</h3>
<span>{childWithProp}</span>
</span>
);
}
}
const Checkout = () => {
return (
<div>
<br />
<br />
<div className="row">
<AddressFormWrapper title="Ship To" showSameAsShipTo={false}>
<span className="col-6">
<AddressForm />
</span>
</AddressFormWrapper>
<AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
<span className="col-6">
<AddressForm />
</span>
</AddressFormWrapper>
</div>
</div>
);
};
In AddressFormWrapper you map over the children and passing props with cloneElement().
As per the DOCS:
Invokes a function on every immediate child contained within children...
But take a good look who are those (immediate) children of AddressFormWrapper:
<AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
<span className="col-6">
<AddressForm />
</span>
</AddressFormWrapper>
In this case its the span element and not AddressForm.
If you render it like this it will work as expected:
<AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
<AddressForm />
</AddressFormWrapper>
Another thing to watch out from, in AddressForm you are setting the state:
disableInputs: props.billToSameAsShipTo
This is inside the constructor and it will only run once. So it will get the initial value but won't get changed.
Either update it in componentDidUpdate or better just use the props directly:
disabled={this.props.billToSameAsShipTo}
Here is a running example:
class AddressForm extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "Joyce",
disableInputs: props.billToSameAsShipTo
};
this.handleBillToSameAsShipToChanged = this.handleBillToSameAsShipToChanged.bind(
this
);
}
handleBillToSameAsShipToChanged() {
this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
}
handleFirstNameChanged(ev) {
this.setState({ firstName: ev.target.value });
}
billToSameAsShipTo() {
if (this.props.showSameAsShipTo === true) {
return (
<span style={{ fontSize: "10pt" }}>
<input
type="checkbox"
checked={this.state.billToSameAsShipTo}
onChange={this.handleBillToSameAsShipToChanged}
/> <span>Same as Ship To</span>
</span>
);
}
}
render() {
return (
<form>
<div className="form-row">
<div className="col-6">
<input
type="text"
className="form-control"
placeholder="First name"
disabled={this.props.billToSameAsShipTo}
value={this.state.firstName}
onChange={this.handleFirstNameChanged.bind(this)}
/>
</div>
</div>
</form>
);
}
}
class AddressFormWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
billToSameAsShipTo: true
};
this.handlebillToSameAsShipToChanged = this.handlebillToSameAsShipToChanged.bind(
this
);
}
handlebillToSameAsShipToChanged() {
this.setState({ billToSameAsShipTo: !this.state.billToSameAsShipTo });
}
render() {
const billToSameAsShipTo = () => {
if (this.props.showSameAsShipTo === true) {
return (
<span style={{ fontSize: "10pt", marginLeft: "20px" }}>
<input
type="checkbox"
checked={this.state.billToSameAsShipTo}
onChange={this.handlebillToSameAsShipToChanged}
/> <span>Same as Ship To</span>
</span>
);
}
};
const childWithProp = React.Children.map(this.props.children, child => {
return React.cloneElement(child, { ...this.state });
});
return (
<span className="col-6">
<h3>
{this.props.title}
{billToSameAsShipTo()}
</h3>
<span>{childWithProp}</span>
</span>
);
}
}
const Checkout = () => {
return (
<div>
<br />
<br />
<div className="row">
<AddressFormWrapper title="Ship To" showSameAsShipTo={false}>
<span className="col-6">
<AddressForm />
</span>
</AddressFormWrapper>
<AddressFormWrapper title="Bill To" showSameAsShipTo={true}>
<AddressForm />
</AddressFormWrapper>
</div>
</div>
);
};
ReactDOM.render(<Checkout />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"/>

Style not updating in component - React [duplicate]

This question already has an answer here:
Update state values with props change in reactjs
(1 answer)
Closed 4 years ago.
I have a main component, and when I pass down a prop to another component, it doesn't update the style. The display is still none, whereas it's meant to update to block since I have changed the prop to true. What might be wrong?
class Apps extends Component {
constructor(props) {
super(props);
// Don't do this!
this.state = { showing: true, Login: false, Signup: false, Members: false };
}
render() {
return (
<div>
<div
className="container"
style={{ display: this.state.showing ? "block" : "none" }}
>
<div>A Single Page web application made with react</div>
</div>
<LoginComponent view={this.state.Login} />
<div className="buttons">
<a href="" ref="login" onClick={this.Login_onclick.bind(this)}>
{this.state.Login ? "back" : "Login"}
</a>
<br />
</div>
</div>
);
}
Login_onclick(e) {
this.setState({ Login: !this.state.Login });
e.preventDefault(); //alert(e.target.value);
this.setState({ showing: !this.state.showing });
// this.setState({ref: !ref});
}
}
Login Component
class LoginComponent extends Component {
constructor(props) {
super(props);
this.state = {
show: this.props.view
};
}
render() {
return (
<div
className="login"
style={{ display: this.state.show ? "block" : "none" }}
>
<h3>Login</h3>
<br />
Username: <input type="text" ref="username" />
<br />
Password <input type="password" ref="password" />
<button value="Login">Login</button>
</div>
);
}
}
You are setting this.state = { show: this.props.view }; when the component is created. Changing the view prop after that will have no effect.
There is no need for you to set show in your state if your want it to update when the prop updates.
class LoginComponent extends Component {
render() {
return (
<div className="login" style={{ display: (this.props.view ? 'block' : 'none') }}>
<h3>Login</h3><br/>
Username: <input type="text" ref="username"/><br/>
Password <input type="password" ref="password"/>
<button value="Login" >Login</button>
</div>
);
}
}

How to prevent react component click event bubble to document?

How to prevent react component click event from bubble up to document ?
I can sure that there must be something was wrong with it!
So, may be I should help myself fix it!
I just have a box component which has a click event listener, and there are some link components in that box which are effected by a document click event. How can I remove this document click event?
Can someone do me a favor?
More details info can be found in following links!
Here is the code link : https://github.com/ant-design/ant-design/issues/6576
Click has no effect, it should link to item3!
Remove document's click event
After that, it works!
What's wrong with this?
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import logo from './logo.svg';
import './App.css';
// import SideBox from './SideBox.js';
// import ContentBox from './ContentBox.js';
/*import SidebarExample from './test.js';*/
// import ReactDOMServer from 'react-dom/server';
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom';
import Item1 from './components/Item1.js';
import Item2 from './components/Item2.js';
import Item3 from './components/Item3.js';
import {Menu, Icon} from 'antd';
import 'antd/dist/antd.css';
const SubMenu = Menu.SubMenu;
const element = <h1>Hello, world</h1>;
const elements = () => {
return(
<h1>Hello, world</h1>
);
};
const routes = [
{
path: '/',
exact: true,
sidebar: () => <div>item1</div>,
main: () => <div><Item1 /></div>
},
{
path: '/item2',
sidebar: () => <div>item2</div>,
main: () => <div><Item2 /></div>
},
{
path: '/item3',
sidebar: () => <div>item3</div>,
main: () => <div><Item3 /></div>
}
]
class App extends Component {
constructor(props) {
super(props);
this.state = {
message: props.message,
styles: props.styles,
Any: props.any,
width: props.width,
theme: 'dark',
current: '1'
};
this.handleMenuClick = this.handleMenuClick.bind(this);
this.handleClick = this.handleClick.bind(this);
};
handleClick(e) {
console.log('click ', e);
this.setState({
current: e.key,
});
};
// ES7 property initializer syntax
handleMenuClick = (e) => {
e.preventDefault();
// e.stopPropagation();
// e.nativeEvent.stopImmediatePropagation();
console.log('this is:', this);
console.log("clicked === \n", e);
if(this.state.styles === "App-SideBox-init"){
this.setState({
message: "e.key",
styles: "App-SideBox-New",
width: "width: 40px;"
});
}
if(this.state.styles === "App-SideBox-New"){
this.setState({
message: "Hello!",
styles: "App-SideBox-init",
width: "width: 300px;"
});
}
console.log("this.state.message === ", this.state.message);
console.log("this.state.styles === ", this.state.styles);
};
componentDidMount() {
/*window.addEventListener('scroll', this.onScroll.bind(this), false);*/
// window.removeEventListener('click', this.handleMenuClick.bind(this), false);
// window.removeEventListener('click', this.handleClick.bind(this), false);
};
render() {
return (
<div className="App">
<div className="App-header">
<img id="img" src={logo} className="App-logo" alt="logo" style={this.props.width}/>
<h2>Welcome to React</h2>
</div>
<div className="App-SideBox">
<div className={this.state.styles}>
<Router>
<div>
<div style={{ display: 'flex' }}>
<div style={{
padding: '10px',
width: '30%',
background: '#f0f0f0'
}}>
<div className="SideBox-body" style={{ display: 'flex' }}>
<Menu
theme={this.state.theme}
onClick={this.handleClick}
style={{ width: 240 }}
defaultOpenKeys={['sub1']}
selectedKeys={[this.state.current]}
mode="inline"
>
<SubMenu
key="sub1"
title={
<span>
<Icon type="mail" />
<span>Navigation One</span>
</span>
}
>
<Menu.Item key="1">
<Link to="/"> item1</Link>
</Menu.Item>
<Menu.Item key="2">
<Link to="/item2">item2</Link>
</Menu.Item>
<Menu.Item key="3">
<Link to="/item3">item3</Link>
</Menu.Item>
</SubMenu>
</Menu>
</div>
</div>
</div>
</div>
</Router>
</div>
{/*onClick={this.handleMenuClick}*/}
<div onClick={this.handleMenuClick} className="App-SideBox-btn">
<span>icon</span>
</div>
</div>
<div className="App-body">
<Router>
<div>
<div>
<div style={{ flex: 1, padding: '10px' }}>
{
routes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.main}
/>
))
}
</div>
</div>
</div>
</Router>
</div>
</div>
);
}
};
App.defaultProps = {
message: 'Hello!',
styles: 'App-SideBox-init'
};
App.propTypes = {
message: PropTypes.string.isRequired,
styles: PropTypes.string.isRequired,
width: PropTypes.string
};
export default App;
Just make all event handlers on the parent component, and then pass them to children by using props!

Categories