React : How can I set the error messages? - reactjs

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.

Related

× TypeError: Cannot read properties of undefined (reading 'map')

When I try to run this code it gives me this error:
× TypeError: Cannot read properties of undefined (reading 'map')
Why does it happen? How can I make it work?
import React from 'react';
import Grid from '#material-ui/core/Grid';
import Product from './Product/Product';
import useStyles from './styles';
const products = [
{id: 1, name: 'Shoes', description: 'Running Shoes.' },
{id: 2, name: 'MacBook', description: 'Apple MacBook.' },
];
const Products = ({ products }) => {
const classes = useStyles();
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))};
</Grid>
</main>
);
};
export default Products;
I had the same error and solved it by first asking if the array existed.
Example:
<Filter>
{ product.color?.map((c) => (
<FilterColor color = {c} key = {c} />
))};
</Filter>
There is a property "products" in your component. That variable has higher priority than the map you have outside, and your .map is using it. I would recommend to rename one of them, to avoid variables with the same name.
Given the error, I would guess that that property wasn't passed to the component.
Also, the parameter of the map lambda is "products" too. Change it to "product", or it will fail.
The properties, products, that you're passing to your component (Products) are undefined. The Map method is taking in account the products that you have passed as properties is not the one that you have created outside the component itself.
If you want to map out the products array that you created outside of your components then just change its name as the array has the same name as the properties passed. If you want to use the products (from the property) then make sure that you're passing the properties in the component.
It's because you have taken the array "products" and the map item "products" by the same name. Change the map item to something else like "product":
const products = [
{id: 1, name: 'Shoes', description: 'Running Shoes.' },
{id: 2, name: 'MacBook', description: 'Apple MacBook.' },
];
const Products = ({ products }) => {
const classes = useStyles();
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products.map((product) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
</Grid>
))};
</Grid>
</main>
);
};
You are getting a blank array[]. For this, you are facing this error. You can solve this problem two ways:
{products && products.map((product) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
</Grid>
))};
Or
{products?.map((product) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
</Grid>
))};
Step 1: Add loading into user page - use useState
{const [loading, setLoading] = useState(true);}
Step 2: Set loading value to false at the end of async fetch function.
If you’re using the try-catch method, put this code line inside the try method:
{ setLoading(false)}
Step 3: Use a unary operation in the inside the render() part of the code
{ loading ? (
<h2>loading</h2>
) : {/* Enter code here */}}
Write it in this way and the issue would be resolved:
<Grid container justify="center" spacing={4}>
{products ?.map((product) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
</Grid>
))};
</Grid>
Make sure to pass the products properties into the Product component. Change this:
{products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
to this:
{products.map((products) => (
<Grid item key={products.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
Why? Because you're mapping an object (products) into a JSX element in your case which is the <Product/> component, so using { product.id } you're trying map an undefined object.
Then make sure the products property you're trying to map out, is correctly passed into the parent component using the Products component.
At first, please check that products are exported. If yes, then change
{products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
to
{products.map((products) => (
<Grid item key={products.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
Because here you map products as products, so you have to set the key like products.id.
You had named each iteration of the map method with products, but you are using product to catch each instance of the iteration.
You can replace the map part like this:
{(products|| []).map(product) => ( // Your code ))}
Make sure properties is not undefined.
typeof products.map() !== undefined
Use:
const Products = ({ products }) => {
const classes = useStyles();
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products && products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))};
</Grid>
</main>
);
};
Just add "products &&". This will check if the map is defined or not.
{products && products.map((product) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))};
Sometimes, it gives an error when you haven't used the variable of useState.
And I have made two other components. 1) Tour 2) Tours
const [tours, setTours ] = useState([]);
Now, I have used this tours variable in App.js file using Tours components.
App.js
I have used tours in the App.js file.
App.js
Now, I'm taking the tours variable for using the .map() function in the Tours.js file.
const Tours = ({ tours }) => {
/...
{tours.map()
.../
Tours.js
You have to check that both files have the same spelling. Otherwise, it gives an error and also doesn’t work the UI of that particular file.
You should try this:
{products.map((product) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product/>
</Grid>
))};
You had this kind of error because you don't pass any information (props) inside the product component. So try this:
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product product={product} />
</Grid>
))};
</Grid>
</main>
);
};
You should basically check the values received are undefined or not.
In Angular, you can use something like this: {{person?.name}}.
Use:
<Grid container justify="center" spacing={4}>
{products && products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))};
</Grid>
Here changes in products && products.map() instead of products.map().
//Just use
{products && products.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))};
// add "products &&" before the map method
const Products = ({ products }) => {
const classes = useStyles();
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products?.map((products) => (
<Grid item key={product.id} item xs={12} sm={6} md={4} lg={3}>
<Product />
</Grid>
))};
</Grid>
</main>
);
};
Use:
{ product.color?.map((c) => (
<FilterColor color = {c} key = {c} />
))};
This would fix the issue, but why does it appear well behind the scene? React won't update the state immediately. This concept is called scheduling, and that is why when you access products.map, products are an empty array.
Check INITIAL_STATE in the reducer.
You must specify an array in the ID object you are calling in the reducer.
Example:
const INTIAL_STATE = {
products: [],
product: { name: "", brand_name: "", prices: "", color: "", images: []},
error: "",
}
product.images.map(img => {
return(
<div key = {img.id} className = "img-itemm">
<img src = {img.image} alt = {img.image} />
</div>
)
})

Retrieve image url from database and display in React using Material-UI

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>
);
}

React : Unable to set the values in the form

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.

How can show the input field on toggle?

I want to show the input field on toggling. The toggling is working but it is not showing the input field. How can I achieve this? I've tried but that is not showing the input field.
Here's the code:
handleToggle=()=>{
console.log('In handletoggle')
this.setState({
milestone_based_payment:!this.state.milestone_based_payment
})
}
percentRate=()=>{
<>
<div className="percent-rate-container">
<Grid container>
<Grid item md={12} lg={12} xs={12}>
<div className="percent-rate">PERCENT RATE</div>
<div>
<InputBase
className={`milestone-percent-rate`}
autoComplete={"off"}
placeholder={"PERCENT RATE"}
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>
)
}
</div>
</Grid>
</Grid>
</div>
</>
}
togglePayment=()=>{
const {milestone_based_payment} = this.state;
console.log('togglepayment')
return(
<div>
<img src={milestone_based_payment ? PRIVATE_SwITCH : PUBLIC_SwITCH} alt="private-public"
onClick={()=>this.handleToggle()}
style={{cursor: "pointer"}}/>
</div>
);
}
You can show/hide input field on true / false condition like this:
showHideInput() {
if(this.state.milestone_based_payment === true) {
return <input />
}
}
// and call the method in other method
// or inline condition
(this.state.milestone_based_payment === true) ? <input /> : <React.Fragment />

React.js: conditional rendering does not load child component

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}
}

Resources