I want to set the values of the form if the edit is true. I am passing the editable state to true and passing the first element of the array but when the component is mounted it didn't show the 0th element values that were sent from the parent component to the child component.
Here's the code :
Milestone.js
import React, { Component } from 'react';
import "./milestone.css";
import MileStoneForm from './mileStoneForm'
import {LEFT_ARROW, ROUTES_PATH } from "../../constants";
export default class MilestoneComp extends Component {
constructor(props) {
super(props);
this.state = {
arr: [],
edit:true
}
}
backPage = () => {
this.props.history.push(ROUTES_PATH.CREATE_PROJECT)
}
handleData=(data)=>{
const {arr}=this.state
arr.push(data)
//localStorage.setItem('mileStoneData',JSON.stringify(arr))
console.log(arr)
}
render() {
return (
<div style={{ background: "#F3F3F3", minHeight: "100vh" }}>
<div className="header-div">
<img
src={LEFT_ARROW}
alt="back"
style={{ cursor: "pointer" }}
className="left-arrow-size left-back-btn-bt-mar"
onClick={this.backPage}
/>
</div>
<MileStoneForm arr={this.state.arr[0]} handleData={this.handleData} edit={this.state.edit}/>
</div>
)
}
}
MileStoneForm.js
import { InputBase,Grid,TextareaAutosize} from '#material-ui/core'
import React, { Component } from 'react'
import { DROP_D } from "../../constants";
import { PRIVATE_SwITCH,PUBLIC_SwITCH } from "../../constants";
import NormalButton from '../../common/component/normalButton';
import DatePicker from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css';
class MileStoneForm extends Component {
constructor(props){
super(props)
this.state={
deliverable_name:"",
due_date:"",
deliverable_notes:"",
milestone_based_payment:false,
deliverable_name_error:"",
due_date_error:"",
deliverable_notes_error:"",
percent_rate:0,
percent_rate_error:""
}
}
componentDidMount(){
console.log('component did mount')
if(this.props.edit===true){
console.log('editable')
if(this.props.arr){
console.log('array')
this.setState({
deliverable_name:this.props.arr.milestoneName,
deliverable_notes:this.props.arr.description,
due_date:this.props.arr.dueDate,
milestone_based_payment:this.props.arr.isMilestoneBasedPayment,
percent_rate:this.props.arr.percentageRate
},()=>{
console.log('edit')
})
}
}
}
render() {
const {deliverable_name,deliverable_name_error,deliverable_notes,deliverable_notes_error,
due_date,due_date_error,milestone_based_payment,}=this.state
return (
<>
<div className="milestone">
<div className="milestone-header">ADD MILESTONE</div>
<Grid container className="milestone-deliverable-name-date">
<Grid item md={6} lg={6} xs={12}>
<div className="milestone-deliverable-name">DELIVERABLE NAME</div>
<InputBase
className={`milestone-input-deliverable-name`}
autoComplete={"off"}
placeholder={"MileStone Name"}
onChange={e=>this.handleChange(e,'deliverable_name')}
value={deliverable_name}
maxLength="100"
autoFocus={true}/>
{deliverable_name_error && (
<div className="input-error-style">{deliverable_name_error}</div>
)}
</Grid>
<Grid item md={6} lg={6} xs={12}>
<div className="milestone-due-date">
DUE DATE
</div>
<label>
<DatePicker
dateFormat="MM/dd/yyyy"
margin="normal"
selected={due_date}
placeholderText="Due Date"
onChange={date=>this.handleChangeDate(date,'due_date')}
maxDate={new Date()}
className={`milestone-input-due-date`}
/>
<img src={DROP_D} alt="drop down" style={{cursor:'pointer'}} className='dropdown-milestone'/>
</label>
{due_date_error && (
<div className="input-error-style">{due_date_error}</div>
)}
</Grid>
<Grid item md={12} lg={12} xs={12}>
<div className="milestone-notes-description">
<div className="milestone-deliverable-notes">DELIVERABLE NOTES</div>
<div className="milestone-description-notes">Add description below</div>
<TextareaAutosize className={`milestone-textarea-description`}
onChange={(e)=>this.handleChange(e,'deliverable_notes')}
value={deliverable_notes}/>
{deliverable_notes_error && (
<div className="input-error-style">{deliverable_notes_error}</div>
)}
</div>
</Grid>
<Grid item md={12} lg={12} xs={12}>
<div className="milestone-payment">MILESTONE BASED PAYMENT?</div>
{this.togglePayment()}
</Grid>
<Grid item md={12} lg={12} xs={12}>
{milestone_based_payment ?<>
<div className="percent-rate">PERCENT RATE</div>
<InputBase
className={`milestone-percent-rate`}
autoComplete={"off"}
placeholder={"20%"}
maxLength="100"
value={this.state.percent_rate}
onChange={(e)=>{this.handleChange(e, "percent_rate")}}
/>
{
this.state.percent_rate_error && (
<div className="input-error-style">{this.state.percent_rate_error}</div>
)
}
</> :''}
</Grid>
<Grid item xs={12} sm={12} md={4} lg={4}></Grid>
<Grid item xs={12} sm={12} md={4} lg={4}></Grid>
<Grid item xs={12} sm={12} md={4} lg={4}>
<div className={milestone_based_payment?"milestone-button":"milestone-button-margin-btm"}>
<NormalButton
buttonValue="ADD"
className="btn-create-project flex-justify"
icon_color="black"
handleButtonAction={()=>this.handleSubmit()}
/>
</div>
</Grid>
</Grid>
</div>
</>
)
}
}
export default MileStoneForm
You are mutating your state with push, never do that. You have to create a new state object.
handleData=(data)=>{
const { arr }= this.state
const nextArr= {...arr, data}
//localStorage.setItem('mileStoneData',JSON.stringify(arr))
this.setState({arr: nextArr});
console.log(arr)
}
The console.log will show the new data anyway, since you are changing that object, but since the object reference will be the same (same object as before), it will not rerender.
Related
I have a list of movies and I want to display the name of the movie and the image attached to that movie. I got image URLs from the web and put them inside an MySQL database(varchar300).
const MovieView = (props: { movie: Movie; }) => {
const {movie} = props;
return (
<Grid item xs={12} sm={6} md={3}
className="movie">
<Link to={`movies/${movie.id}`}>
<Paper elevation={3} className="movie-paper">
<div>
<h2>{movie.title}</h2>
<img src="movie.image" alt=""/>
</div>
</Paper>
</Link>
</Grid>
);
In order to do this, you need the src to be in template form (using squiggly brackets {}) instead of quotes. Try this instead
const MovieView = (props: { movie: Movie; }) => {
const {movie} = props;
return (
<Grid item xs={12} sm={6} md={3}
className="movie">
<Link to={`movies/${movie.id}`}>
<Paper elevation={3} className="movie-paper">
<div>
<h2>{movie.title}</h2>
<img src={movie.image} alt=""/>
</div>
</Paper>
</Link>
</Grid>
);
}
I am trying to set the error message but as in the state I've passed initial Values object, I'm unable to set the state. How can I achieve this in react? How can I set the error messages in the set State function? Can someone help me?. After console logging it is showing the error in the console. But how can I show this on screen? Thanks in advance.
Here's the code :
const initialValues={
deliverable_name:"",
due_date:new Date(),
deliverable_notes:"",
milestone_based_payment:false,
deliverable_name_error:"",
due_date_error:"",
deliverable_notes_error:""
}
class MileStoneForm extends Component {
constructor(props){
super(props)
this.state={
initialValues
}
}
handleChange=(e,type)=>{
let error=true
if(type==='deliverable_notes'){
if(e.target.value.length>300){
error=false
this.setState({deliverable_notes_error:'Description must contain less than 300 characters'})
}
}
if(error){
this.setState({
[type]:e.target.value,
[type+"_error"]: ""
})
}
}
validateMileStoneName=()=>{
if(!this.state.initialValues.deliverable_name || this.state.initialValues.deliverable_name.length< 4 ){
console.log("enter deliverable name")
this.setState({deliverable_name_error:'Please enter deliverable name'})
}
}
validateMileStoneDueDate=()=>{
if(!this.state.initialValues.due_date){
this.setState({due_date_error:'Please enter due date'})
}
}
validateMileStoneDescription=()=>{
if(!this.state.initialValues.deliverable_notes){
this.setState({deliverable_notes_error:'Please enter description'})
}
}
handleSubmit=(data)=>{
this.validateMileStoneName()
this.validateMileStoneDescription()
console.log(data)
this.props.handleData(data)
}
render() {
const {initialValues}=this.state
return (
<>
<div className="milestone">
<div className="milestone-header">ADD MILESTONE</div>
<Grid container className="milestone-deliverable-name-date">
<Grid item md={6} lg={6} xs={12}>
<div className="milestone-deliverable-name">DELIVERABLE NAME</div>
<div>
<InputBase
className={`milestone-input-deliverable-name`}
autoComplete={"off"}
placeholder={"MileStone Name"}
onChange={e=>this.handleChange(e,'initialValues.deliverable_name')}
value={initialValues.deliverable_name}
maxLength="100"
autoFocus={true}/>
{initialValues.deliverable_name_error && (
<div className="input-error-style">{initialValues.deliverable_name_error}</div>
)}
</div>
</Grid>
<Grid item md={6} lg={6} xs={12}>
<div>
<div className="milestone-due-date">
DUE DATE
</div>
<InputBase
className={`create-project-due-date`}
autoComplete={"off"}
type="date"
placeholder={"DUE DATE"}
onChange={e=>this.handleChange(e,'due_date')}
value={initialValues.due_date}/>
{initialValues.due_date_error && (
<div className="input-error-style">{initialValues.due_date_error}</div>
)}
</div>
</Grid>
</Grid>
<Grid container className="milestone-deliverable-notes">
<Grid item md={12} lg={12} xs={12}>
<div className="milestone-notes-description">
<div className="milestone-deliverable-notes">DELIVERABLE NOTES</div>
<div className="milestone-description-notes">Add description below</div>
<TextareaAutosize className={`milestone-textarea-description`}
onChange={(e)=>this.handleChange(e,'deliverable_notes')}
value={initialValues.deliverable_notes}/>
{initialValues.deliverable_notes_error && (
<div className="input-error-style">{initialValues.deliverable_notes_error}</div>
)}
</div>
</Grid>
</Grid>
<div className={initialValues.milestone_based_payment?"milestone-button":"milestone-button-margin-high"}>
<Grid container spacing={10}>
<Grid item xs={12} sm={12} md={4} lg={4}></Grid>
<Grid item xs={12} sm={12} md={4} lg={4}></Grid>
<Grid item xs={12} sm={12} md={4} lg={4}>
<div className="milestone-button-margin-btm">
<NormalButton
buttonValue="ADD"
className="btn-create-project flex-justify"
icon_color="black"
handleButtonAction={()=>this.handleSubmit(initialValues)}
/>
</div>
</Grid>
</Grid>
</div>
</div>
</>
)
}
}
export default MileStoneForm
You're mixing initialValue in the state which is an object, with the properties you're setting. So after an error, your state is actually looking like this:
this.state = {
initialValue: [object] // this could potentially mutate (a bug).
deliverable_notes_error: "Please set ....",
}
What you need to do is use the spread syntax to set your initial state:
constructor(props) {
this.state = {
...initialValues // your state is now a soft clone of the object
};
}
render(props) {
const { deliverable_notes_error } = this.state;
return (
{ deliverable_notes_error && <div>{ deliverable_notes_error }</div> }
);
}
3 things
I would move the error messages inside State.
deliverable_name_error:"",
due_date_error:"",
deliverable_notes_error:""
Update this.setState - already done
Display error messages - also doing.
Basically, if you move everything from initialValues into state, your code should work.
I need to render the child component during conditional rendering. It is not displayed in the current version.
If you insert into the html markup, then there are no errors, the component is rendered normally.
What could be my mistake with conditional rendering?
Parent component:
export default class App extends Component {
data = {
email: "a#b.net",
password: "adc"
}
state = {
email: "",
password: ""
}
emailChange=(e)=>{
this.setState({email: e.target.value});
}
passwordChange=(e)=>{
this.setState({password: e.target.value});
}
buttonSubmit=(e)=>{
let p=this.state.email===this.data.email
&& this.state.password===this.data.password ? <div><Page1/></div> : alert('poi');
e.preventDefault()
}
render() {
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<form noValidate autoComplete="off"
onSubmit={this.buttonSubmit}>
<div style={{marginTop:"150px"}}>
<Grid container spacing={2}>
<Grid item xs={12} >
<TextField
id="outlined-name"
label="e-mail"
variant="outlined"
value={this.state.email}
onChange={this.emailChange}/>
</Grid>
<Grid item xs={12} >
<TextField
className="MuiInput-input"
id="outlined-name"
label="password"
variant="outlined"
value={this.state.password}
onChange={this.passwordChange}/>
</Grid>
<Grid item xs={12}>
<Button
style={{width:'210px'}}
type="submit"
fullWidth
variant="contained"
color="primary"
>
Enter
</Button>
</Grid>
</Grid>
</div>
</form>
</Container>
);
}
}
Child component that is not rendered during conditional rendering:
const Page1 =()=>{
return (
<div style={{height: "100vh"}}>
<Header/>
<Grid container spacing={3}>
<Grid item xs={3}>
<Paper><ButtonPage/></Paper>
</Grid>
<Grid item xs={12}>
<Paper><ButtonWindow /></Paper>
</Grid>
</Grid>
</div>
);
}
You're just assigning your child component conditionally to a local variable p, that you never render. I'd change your logic like this:
buttonSubmit=(e)=>{
let p=(this.state.email===this.data.email &&
this.state.password===this.data.password) ?
<div><Page1/></div>
:
null;
this.setState({p: p});
e.preventDefault()
}
render() {
...
{this.state.p}
}
i am using react slick...and using class component, now i just want to convert class component to functional component....because in these component i need to use another function component...it getting some problem....could any one help on this thing...below the class i have used (renderArrows =) this i thing is getting problem...
this is the link of code sand box : https://codesandbox.io/s/jovial-smoke-ndzhj
import React, {Component } from 'react';
import { Col, Row } from 'react-grid-system';
import { ButtonBase } from '#material-ui/core';
import ArrowBackIosIcon from '#material-ui/icons/ArrowBackIos';
import ArrowForwardIosIcon from '#material-ui/icons/ArrowForwardIos';
import Slider from "react-slick";
class Example extends Component {
renderArrows = () => {
return (
<div className="slider-arrow">
<ButtonBase
className="arrow-btn prev"
onClick={() => this.slider.slickPrev()}
>
<ArrowBackIosIcon />
</ButtonBase>
<ButtonBase
className="arrow-btn next"
onClick={() => this.slider.slickNext()}
>
<ArrowForwardIosIcon />
</ButtonBase>
</div>
);
};
render() {
return (
<div>
<Col xl={12} lg={12} md={12} sm={12} xs={12} className="desktop-slider">
<div className="education-level main-boxes boxex-bottom">
<Row className="row">
<Col xl={4} lg={4} md={4} sm={12} xs={12}>
<div className="index-box-head horizontal-box-head">
1. Education Level
</div>
</Col>
<Col xl={8} lg={8} md={8} sm={12} xs={12}>
<div style={{ position: "relative" }}>
{this.renderArrows()}
<Slider
ref={c => (this.slider = c)}
dots={false}
arrows={false}
centerMode={true}
slidesToShow={1}
infinite={false}>
<li>Home
</li>
<li>About Us
</li>
<li>Gallery
</li>
</Slider>
</div>
</Col>
</Row>
</div>
</Col>
</div>
);
}
}
export default Example;
This can be the best answer
I tried and made this work with you sample
Check please
https://codesandbox.io/s/infallible-turing-wgvrb?file=/src/App.js
import React, { useRef } from "react";
import { Col, Row } from "react-grid-system";
import { ButtonBase } from "#material-ui/core";
import ArrowBackIosIcon from "#material-ui/icons/ArrowBackIos";
import ArrowForwardIosIcon from "#material-ui/icons/ArrowForwardIos";
import Slider from "react-slick";
const Example = () => {
const customSlider = useRef();
const renderArrows = () => {
return (
<div className="slider-arrow">
<ButtonBase
className="arrow-btn prev"
onClick={() => customSlider.current.slickPrev()}
>
<ArrowBackIosIcon />
</ButtonBase>
<ButtonBase
className="arrow-btn next"
onClick={() => customSlider.current.slickNext()}
>
<ArrowForwardIosIcon />
</ButtonBase>
</div>
);
};
return (
<div>
<Col xl={12} lg={12} md={12} sm={12} xs={12} className="desktop-slider">
<div className="education-level main-boxes boxex-bottom">
<Row className="row">
<Col xl={4} lg={4} md={4} sm={12} xs={12}>
<div className="index-box-head horizontal-box-head">
1. Education Level
</div>
</Col>
<Col xl={8} lg={8} md={8} sm={12} xs={12}>
<div style={{ position: "relative" }}>
{renderArrows()}
<Slider
ref={slider => (customSlider.current = slider)}
dots={false}
arrows={false}
centerMode={true}
slidesToShow={1}
infinite={false}
>
<li>Home</li>
<li>About Us</li>
<li>Gallery</li>
</Slider>
</div>
</Col>
</Row>
</div>
</Col>
</div>
);
};
export default Example;
class component to functional component:
import React, { Component } from 'react'
import { Col, Row } from 'react-grid-system'
import { ButtonBase } from '#material-ui/core'
import ArrowBackIosIcon from '#material-ui/icons/ArrowBackIos'
import ArrowForwardIosIcon from '#material-ui/icons/ArrowForwardIos'
import Slider from 'react-slick'
const renderArrows = () => {
return (
<div className="slider-arrow">
<ButtonBase
className="arrow-btn prev"
onClick={() => this.slider.slickPrev()}
>
<ArrowBackIosIcon />
</ButtonBase>
<ButtonBase
className="arrow-btn next"
onClick={() => this.slider.slickNext()}
>
<ArrowForwardIosIcon />
</ButtonBase>
</div>
)
}
const Example = () => {
return (
<div>
<Col xl={12} lg={12} md={12} sm={12} xs={12} className="desktop-slider">
<div className="education-level main-boxes boxex-bottom">
<Row className="row">
<Col xl={4} lg={4} md={4} sm={12} xs={12}>
<div className="index-box-head horizontal-box-head">
1. Education Level
</div>
</Col>
<Col xl={8} lg={8} md={8} sm={12} xs={12}>
<div style={{ position: 'relative' }}>
{renderArrows()}
<Slider
ref={c => (this.slider = c)}
dots={false}
arrows={false}
centerMode={true}
slidesToShow={1}
infinite={false}
>
<li>Home</li>
<li>About Us</li>
<li>Gallery</li>
</Slider>
</div>
</Col>
</Row>
</div>
</Col>
</div>
)
}
export default Example
One more thing, I don't seem to get from where this.slider.slickNext() came from. I think it would be this.Slider.slickPrev().
If I'm right, then for functional component, change it to Slider.slickPrev()
In my componentDidMount() I am calling this.props.getPost(soloPost._id); in which case will use redux. This is working correctly, because if I console.log it I get my desired result. But componentDidMount() is looping continuously. I have a feeling i'm missing something on the component lifecycle
class PostItem extends Component {
constructor(props) {
super(props);
this.state = {
showCommentField: false
};
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState(prevState => ({
showCommentField: !prevState.showCommentField
}));
}
componentDidMount() {
const { soloPost } = this.props;
this.props.getPost(soloPost._id);
console.log(this.props.comments);
}
render() {
const { soloPost, classes } = this.props;
const postContent = (
<div>
<CommentFeed postId={soloPost._id} comments={soloPost.comments} />
<CommentForm postId={soloPost._id} />
</div>
);
return (
<div className={classes.section}>
<GridContainer justify="center">
<GridItem xs={12} sm={10} md={8}>
<hr />
<Card plain profile className={classes.card}>
<GridContainer>
<GridItem xs={12} sm={2} md={2}>
<CardAvatar plain profile>
<img src={soloPost.avatar} alt="..." />
</CardAvatar>
</GridItem>
<GridItem xs={12} sm={8} md={8}>
<h4 className={classes.cardTitle}>{soloPost.name}</h4>
<p className={classes.description}>{soloPost.text}</p>
</GridItem>
</GridContainer>
</Card>
</GridItem>
</GridContainer>
<GridContainer justify="center">
<Tooltip
id="tooltip-tina"
title="Reply to comment"
placement="top"
classes={{ tooltip: classes.tooltip }}
>
<Button
color="primary"
simple
className={classes.footerButtons}
onClick={this.onClick}
>
<Reply className={classes.footerIcons} /> Reply
</Button>
</Tooltip>
</GridContainer>
{this.state.showCommentField ? (
<GridContainer justify="center">
<GridItem xs={12} sm={5} md={5} lg={5}>
{postContent}
</GridItem>
</GridContainer>
) : (
<div />
)}
</div>
);
}
}
PostItem.defaultProps = {
showActions: true
};
PostItem.propTypes = {
soloPost: PropTypes.object.isRequired,
getPost: PropTypes.func.isRequired,
comments: PropTypes.object.isRequired
};
const mapStateToProps = state => ({ comments: state.post.post });
export default connect(
mapStateToProps,
{ getPost }
)(withStyles(sectionBlogInfoStyle)(PostItem));
EDIT
I just tried this and it looped as well.
shouldComponentUpdate(nextProps, nextState) {
const { soloPost } = this.props;
if (nextProps !== this.props) {
this.props.getPost(soloPost._id);
return true;
}
if (nextState !== this.state) {
return true;
}
}