I want to make it necessary to mark the React js checkbox component. I want to show warning icon when Checkbox is not selected. How can I do that? I shared the component I wrote below.
React checkbox component
const CheckBox = ({
question,
formKey,
valid = true,
validationError = 'Field is required',
}) => {
const dispatch = useDispatch()
const reduxValue = useSelector(state => state.app.forms.f3[formKey].value)
const isSaved = useSelector(state => state.app.forms.f3[formKey].saved)
const [input, setInput] = useState({
value: reduxValue,
valid: true,
})
return (
<>
<Row>
<Col className="col-1">
<label className="orange-checkbox-container">
<input
type="checkbox"
name={formKey}
onChange={(e) => changeSelected(e)}
checked={input.value}
/>
<span className="checkmark"></span>
</label>
</Col>
<Col className="col-11">
<p>{question}</p>
</Col>
</Row>
<div
className="invalid-feedback"
style={{
width: '8rem',
}}
>
{validationError}
</div>
</>
);
}
I will improve on Han's answer. You should hide the whole div if there the checkbox was selected. I improved how the onChange works and separated states. Using hooks you can separate your state values rather than the approach used in classes where you have 1 object this.state = {} to manage the whole state of that component.
const CheckBox = ({
question,
formKey,
// valid = true,
validationError = 'Field is required',
}) => {
const dispatch = useDispatch();
const reduxValue = useSelector(state => state.app.forms.f3[formKey].value);
const isSaved = useSelector(state => state.app.forms.f3[formKey].saved);
const [checked, setChecked] = useState(reduxValue);
const [valid, setValid] = useState(true);
const handleCheckedChange = event => {
setChecked(event.target.checked);
}
return (
<>
<Row>
<Col className="col-1">
<label className="orange-checkbox-container">
<input
type="checkbox"
name={formKey}
onChange={handleCheckedChange}
checked={checked}
/>
<span className="checkmark"></span>
</label>
</Col>
<Col className="col-11">
<p>{question}</p>
</Col>
</Row>
{!checked &&
<div
className="invalid-feedback"
style={{
width: '8rem',
}}
>
{validationError}
</div>
}
</>
);
}
First you should declare the initial state of valid to false and display validationError if input.value is false I assume your onChange is changing the input.value.
const [input, setInput] = useState({
value: reduxValue,
valid: false,
})
<div
className='invalid-feedback'
style={{
width: '8rem',
}}
>
{!input.value && validationError}
</div>
Related
Keep getting this error when trying to submit a form in React
Uncaught TypeError: queryList.map is not a function
This is the code:
const [query, setQuery] = useState();
const [queryList, setQueryList] = useState([]);
const [response, setResponse] = useState([]);
const [responseList, setResponseList] = useState([]);
const createQuery = () =>{
setQueryList(
{query}
);
alert(queryList)
Axios.post('http://localhost:3001/createQuery', {
query
}).then((res)=>{
setResponse(res)
setResponseList(...responseList, {res})
})
}
return(
<div>
{queryList && queryList.map((e) => {
return(
<p className="ml-52 text-white text-xl">{e.query}</p>
)
})}
<form>
<textarea onChange={(event)=>{
setQuery(event.target.value)
}}
type="text" name="name" autoComplete="off" placeholder="Ask a question" className = "caret-gray-200 bg-gray-800 shadow-md h-20 w-5/6 inset-x-0 bottom-6 absolute left-36 items-center snap-center text-xl p-6" />
<button onClick={createQuery} type="submit" name="submit" className="text-white inset-x-0 bottom-6 absolute bg-transparent w-20 h-20 ml-auto mr-28 focus:outline-none focus:none">
<AiOutlineSend size="28" />
</button>
</form>
</div>
)
After I submit the form, I need the query mapped
I know I didn't mention any other errors, but if you see them, please let me know!
By the way, all the other posts didn't work for me.
Thanks :)
When you set the queryList you will need to set it to an array by using array brackets. Also by the name i'm assuming you want an array of all queries so you will need to include previous queries that are already stored in queryList.
setQueryList([...queryList, { query }]);
This is what your setter function should look like.
Aside from this the alert function will not work since queryList is not updated in time to be used so I would recommend you to just use query in alert instead of queryList.
Also because of the way you use forms the page will be redirected, this is solved by using onSubmit event and using the preventDefault() function.
const [query, setQuery] = useState();
const [queryList, setQueryList] = useState([]);
const [response, setResponse] = useState();
const [responseList, setResponseList] = useState([]);
const createQuery = (event) => {
event.preventDefault();
setQueryList([...queryList, { query }]);
alert(query);
Axios.post('http://localhost:3001/createQuery', {
query,
}).then((res) => {
setResponse(res);
setResponseList(...responseList, { res });
});
};
return (
<div>
{queryList &&
queryList.map((e) => {
return (
<p className='ml-52 text-white text-xl'>{e.query}</p>
);
})}
<form onSubmit={createQuery}>
<textarea
onChange={(event) => {
setQuery(event.target.value);
}}
type='text'
name='name'
autoComplete='off'
placeholder='Ask a question'
className='caret-gray-200 bg-gray-800 shadow-md h-20 w-5/6 inset-x-0 bottom-6 absolute left-36 items-center snap-center text-xl p-6'
/>
<button
type='submit'
name='submit'
className='text-white inset-x-0 bottom-6 absolute bg-transparent w-20 h-20 ml-auto mr-28 focus:outline-none focus:none'
/>
</form>
</div>
);
I have not tested anything with axios but this code should work.
In addition to this question
I am trying to map individually a state to another state to store the amountToPay object to get the sum of it. The problem is every time it renders the onChange function. It stores every state as object as you can see here: .
What I want to happen is to only get [434] instead of ['','4','43','434']
So I can .reduce the array to get the sum.
My method on storing the array object to another state is this
const [amountToPay, setAmountToPay] = useState("")
console.log("AMOUNT TO PAY", amountToPay)
useEffect(() => {
serviceItem.map((item) => (
setAmountToPay([...amountToPay, item.amountToPay])
))
}, [serviceItem])
useEffect(() => {
serviceItem.map((item) => (
setAmountToPay([...amountToPay, item.amountToPay])
))
}, [serviceItem])
You can check the whole code here CodeSandbox code.Any help is appreciated :)
There are several things I suggest you to do:
Add some id property to your serviceItem. You can use UUID, nanoid, or even Date.now()
Remove const [amountToPay, setAmountToPay] = useState([]);
Use values directly from serviceItem collection. In order to do this you need to create onChange handler, it will be something like this
const handleChange = (id) => (nextAmount) => {
setServiceList(prevValue => {
return prevValue.map(item => item.id === id ? { ...item, amount: nextAmount } : item)
})
}
And amount to pay can be easily got from serviceItem collection, without effects or other states
const procedurePriceTotal = serviceItem.reduce(
(acc, item) => (acc = acc + item.amount),
0
);
this is happening because you are setting serviceItem on onChange method
and use passed serviceItem as deps array to useeffect in which you are setting amountToPay.
so on every change it's appending in array
Rather then setting amount in useEffect, make a method and call on remove/add button so it will only call after user is finished typing. you can also place a button 'Save' or 'calculate Amount' and call handleSetAmountToPAY method which will update amount.
import React, { useState, useMemo, useEffect } from "react";
export default function App() {
//Values
const [serviceItem, setServiceList] = useState([
{ serviceValue: "", quantityValue: "", amountToPay: "" }
]);
console.log("SERVICE ITEM", serviceItem);
//Add item function
const handleItemAdd = () => {
setServiceList([
...serviceItem,
{ serviceValue: "", quantityValue: "", amountToPay: "" }
]);
handleSetAmountToPAY(serviceItem)
};
//Remove item function
const handleItemRemove = (index) => {
const list = [...serviceItem];
list.splice(index, 1);
setServiceList(list);
handleSetAmountToPAY(list)
};
//Get Values
const handleGetValues = (e, index) => {
const { name, value } = e.target;
const list = [...serviceItem];
list[index][name] = value;
setServiceList(list);
};
//Saving state to another state
const [amountToPay, setAmountToPay] = useState([]);
console.log("AMOUNT TO PAY", amountToPay);
const handleSetAmountToPAY = (list) => {
list && list.map((item) =>
setAmountToPay([...amountToPay, item.amountToPay])
);
}
//Add total amount
const procedurePriceTotal = amountToPay.reduce(
(index, value) => (index = index + value),
0
);
console.log("TOTAL PRICE", procedurePriceTotal);
return (
<div className="App">
{serviceItem.map((singleItem, index) => (
<div class="row form-row">
<div class="col-12 col-md-6 col-lg-4">
<div class="form-group">
<label>
Service <span class="text-danger">*</span>
</label>
<input
name="serviceValue"
type="text"
class="form-control"
value={singleItem.serviceValue}
placeholder="Tooth Extraction"
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="form-group">
<label>
Quantity <span class="text-danger">*</span>
</label>
<input
name="quantityValue"
type="text"
class="form-control"
placeholder="1"
value={singleItem.quantityValue}
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="form-group">
<label>
Amount (₱)<span class="text-danger">*</span>
</label>
<input
name="amountToPay"
type="number"
class="form-control"
placeholder="500"
value={singleItem.amountToPay}
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-2">
<div class="add-more">
<br />
{serviceItem.length !== 1 && (
<button
type="submit"
onClick={() => handleItemRemove(index)}
className="btn btn-primary rx-pr"
>
<i className="fas fa-plus" /> Remove Item
</button>
)}
</div>
</div>
</div>
))}
{/* Add Item */}
<div className="add-more-item rx-pr">
<button
type="submit"
onClick={handleItemAdd}
className="btn btn-primary rx-pr"
>
<i className="fas fa-plus" /> Add Item
</button>
</div>
</div>
);
}
I was doing it the wrong way.
I solved it by mapping the serviceItem then using reduce to get the sum instead of putting it again into a separate array of object then mapping it again to get the sum.
const newNumberArray = serviceItem.map(function(item) {
return parseInt(item.amountToPay)
})
const totalAmountPaid = newNumberArray.reduce((index,value) => index = index + value, 0 )
Thanks for all the help and suggestion!
I want to save the progress state of Mobile Stepper, so that when user logged out, the user can resume from the place where he left. If progress bar is filled 2% then when next time the user login he should resume from 2% not from 0.
Here is the code:
import React, { useState, useReducer, useEffect } from "react";
import { Button, Typography, Grid, CircularProgress, Paper } from '#mui/material/';
import guardianOptions from '../../../constants/guardianOptions.js';
import studentClasses from '../../../constants/studentClasses.js';
import Stepper from '#mui/material/Stepper';
import Step from '#mui/material/Step';
import StepLabel from '#mui/material/StepLabel';
import MobileStepper from '#mui/material/MobileStepper';
import StepContent from '#mui/material/StepContent';
import { makeStyles, withStyles, createStyles } from '#mui/styles';
import { purple } from '#mui/material/colors';
import TextField from '#mui/material/TextField';
import useStyles from './styles';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormHelperText from '#mui/material/FormHelperText';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
const User= (props) => {
const { activeStep: lastActiveStep, userId } = props;
const classes = useStyles();
const [guardianType, setGuardianType] = useState(0);
const [activeStep, setActiveStep] = useState(lastActiveStep || 0);
const [guardianRelationOptions, setGuardianRelationOptions] = useState(0);
const [guardianDetailsForm, setGuardianDetailsForm] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{}
);
const [studentDetailsForm, setStudentDetailsForm] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{ s_firstName: '', s_lastName: '', s_age: '', s_class: '' }
);
React.useEffect(() => {
async function updateActiveStep() {
// this is just pseudo code - whatever your endpoint looks like
await window.fetch('http://localhost:8080/api/user/:userId', {
method: 'PUT',
body: JSON.stringify({activeStep})
})
}
updateActiveStep()
}, [activeStep]);
function getSteps(){
return [<b style={{color:'purple'}}>'Personal Details'</b>,
<b style={{color:'purple'}}>'Relation'</b>,
<b style={{color:'purple'}}>'Guardian Details'</b>];
}
const steps = getSteps();
function onGuardianTypeChangeChange(event) {
// setAge(event.target.value);
setGuardianType(event.target.value);
let _guardianRelationOptions = guardianOptions.find(options => options.value === event.target.value);
setGuardianRelationOptions(_guardianRelationOptions.relationships);
}
const handleGuardianDeatilsInput = evt => {
const name = evt.target.name;
const newValue = evt.target.value;
setGuardianDetailsForm({ [name]: newValue });
};
const handleGuardianDetailsSubmit = evt => {
evt.preventDefault();
let data = { guardianDetailsForm };
props.onGuardianDetails(guardianDetailsForm)
console.log(data + "new user");
// console.log( props.onGuardianDetails(guardianDetailsForm) + "gana bajao");
setActiveStep((prevActiveStep) => prevActiveStep+1);
setGuardianDetailsForm();
}
function getStepContent(step) {
switch (step) {
case 0: if (!props.user.s_firstName) {
return (<div>
<form id ="form-step0" className={classes.root} onSubmit={handleGuardianDetailsSubmit} noValidate autoComplete="off">
<TextField
id="outlined-basic"
name="s_firstName"
label="First Name"
variant="outlined"
defaultValue={guardianDetailsForm.s_firstName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-basic"
name="s_lastName"
label="Last Name"
variant="outlined"
defaultValue={guardianDetailsForm.s_lastName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-number"
label="Age"
name="s_age"
defaultValue={guardianDetailsForm.s_age}
type="number"
InputLabelProps={{
shrink: true,
}}
onChange={handleGuardianDeatilsInput}
variant="outlined"
/>
<FormControl variant="outlined" className={classes.formControl}
sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Class</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={guardianDetailsForm.s_class}
onChange={handleGuardianDeatilsInput}
label="Class"
name="s_class"
>
{studentClasses.map(c =>
<MenuItem key={c.value} value={c.value}>{c.name}</MenuItem>
)}
</Select>
{/* <Button variant="contained" type="submit" color="primary" >NEXT</Button> */}
</FormControl>
</form>
</div> )}
;
case 1: if (!props.user.g_relationship) {
return ( <div>
<form id="form-step1" className={classes.root} onSubmit={handleGuardianDetailsSubmit} noValidate autoComplete="off">
<FormControl variant="outlined" className={classes.formControl}
sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Relationship</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
onChange={onGuardianTypeChangeChange}
label="Relationship"
>
{guardianOptions.map(type =>
<MenuItem key={type.value} value={type.value}>{type.name}</MenuItem>
)}
</Select>
</FormControl>
{guardianRelationOptions ?
<FormControl variant="outlined" className={classes.formControl}
sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Relation</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
// value={age}
name="g_relationship"
value={guardianDetailsForm.g_relationship}
onChange={handleGuardianDeatilsInput}
label="Relation"
>
{guardianRelationOptions.map(type =>
<MenuItem key={type.value} value={type.value}>{type.name}</MenuItem>
)}
</Select>
</FormControl> : null
}
{!g_relationship} onClick={() => props.onGuardianDetails({g_relationship})}>NEXT</Button> */}
{/* <Button variant="contained" color="primary" type="submit">NEXT</Button> */}
</form>
</div> )}
;
case 2:
return ( <div>
<form id="form-step2" className={classes.root} onSubmit={handleGuardianDetailsSubmit} noValidate autoComplete="off">
<TextField
id="outlined-basic"
name="g_firstName"
label="First Name"
variant="outlined"
defaultValue={guardianDetailsForm.g_firstName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-basic"
name="g_lastName"
label="Last Name"
variant="outlined"
defaultValue={guardianDetailsForm.g_lastName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-number"
label="Age"
name="g_age"
defaultValue={guardianDetailsForm.g_age}
type="number"
InputLabelProps={{
shrink: true,
}}
onChange={handleGuardianDeatilsInput}
variant="outlined"
/>
</form>
</div>)
;
default:
return 'welcome lets fill the progress.' ;
}
}
return (
<div className={classes.root} align="center">
<div className = {classes.actionsContainer}>
<Paper square elevation={0}>
<Typography>{getStepContent(activeStep)}</Typography>
</Paper>
<MobileStepper
variant="progress"
steps= {4}
position="bottom"
activeStep={activeStep}
sx={{ minWidth: 400, flexGrow: 1 }}
nextButton={
<>
<Button size="small" onClick={handleGuardianDetailsSubmit} type="submit"
form={`form-step${activeStep}`}>
{activeStep === steps.length-1? 'Finish' : 'Next'}
</Button>
</>
}
/>
</div>
</div>
);
}
export default User;
If user refreshes or reload the page, he should see the progress from where he has left.
#Tanya
So you are saying that your user can authorize. When this happens do you receive any data about the user? Can you update the user data via POST or PUT request? If so, I'd store the active step with this data.
Assuming you have some user data that you receive when the user signs in:
// user - user data
export default function BuildAdStepper({user}) {
const { activeStep: lastActiveStep, userId } = user
const classes = useStyles();
const theme = useTheme();
const [activeStep, setActiveStep] = React.useState(lastActiveStep || 0);
React.useEffect(() => {
async function updateActiveStep() {
// this is just pseudo code - whatever your endpoint looks like
await window.fetch('/yourEndpoint/:userId', {
method: 'PUT',
body: JSON.stringify({activeStep})
})
}
updateActiveStep()
}, [activeStep])
/* ... rest of your component * /
}
I have an array of football matches that I'll like users to enter their predictions. The issue I'm facing is that I'm unable to get save this form into an array.
Here is the visual look of the form.
Current form output
Here's my react code
import React, { useEffect, useState } from 'react';
import { Row, Col } from 'antd';
import moment from 'moment';
import { PredictTag, PotTitle, AmountInput } from '../../styles/styling';
import { Form, Container } from 'react-bootstrap';
import { produce } from "immer";
const StakeForm = (props) => {
const { potter } = props;
const [inputList, setInputList] = useState([
]);
const pots = potter.pot.findPot.pot;
useEffect(() => {
pots.slice(0, 4).map(() => {
setInputList(currentInput => [{
pot: {home_score: '', away_score: ''}
}
]);
})
},[]);
return (
<Container>
<Row justify="space-around" align="middle">
<Col span={12}><PotTitle>{potter.pot.findPot.pot_name}</PotTitle></Col>
<Col span={12} className="mt-2">
<PredictTag>Home</PredictTag>
<PredictTag>Away</PredictTag>
</Col>
</Row>
<Form>
{pots.map(({_id, home_team, away_team, date, time}) => (
<Row justify="space-around" align="middle" key={_id}>
<Col span={12}>
<h6>{home_team} vs {away_team}</h6>
<p>{moment(date).format("MMM Do, YYYY")} | {moment(time).format('LT')}</p>
</Col>
<Col span={12}>
{inputList.map((item, index) => (
<Container key={index}>
<Row>
<Col span={12}>
<Form.Group controlId="home" style={{ width: 100, }}>
<Form.Control as="select"
onChange={(e) => {
const home_score = e.target.value;
setInputList((currentInput) =>
produce(currentInput, (v) => {
v[index].pot.home_score = home_score;
})
)}}
>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
</Col>
<Col span={12}>
<Form.Group controlId="away" style={{ width: 100, float: 'left' }}>
<Form.Control as="select"
onChange={(e) => {
const away_score = e.target.value;
setInputList((currentInput) =>
produce(currentInput, (v) => {
v[index].pot.away_score = away_score;
})
)}}
>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
</Col>
</Row>
</Container>
))}
</Col>
</Row>
))}
<pre>{JSON.stringify(inputList, null, 2)}</pre>
</Form>
</Container>
);
};
export default StakeForm;
Currently, all home_score and away_score fields only return a single output rather than creating an object for each of the matches. Please do help me on this.
I have a form which has a input field called admissionNumber and the button. In input field when user enter number and click the button then function getAllStudent filter the an array . If admission number match with entered number then other fields (fullname and faculty) automatically filled . How can I do this ? Please someone help me to do this . Thank you
getAllStudents function which return students details (admissionNumber,fullname,faculty)
getAllStudents(user._id, token).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues(data);
}
});
form fields
<input
type="text"
onChange={(event) => {
setSearchTerm(event.target.value);
}}
className="form-control offset-md-2 col-md-6"
placeholder="Admission Number"
required
maxLength="5"
/>
<button
// onClick={}
className="btn rounded ml-4"
>
Verify
</button>
</div>
<div className="bg-dark rounded">Personal Details</div>
<div className="row form-group ">
<input
type="text"
name="studentFullName"
className="form-control mt-2 offset-md-2 col-md-8"
placeholder="Student Name"
/>
<input
type="text"
name="faculty"
className="form-control mt-2 offset-md-2 col-md-8"
/>
</div>
You should pass a function to button onClick prop.
Assuming you using a functional component and a state with students, currentUser and searchTerm you can do something like that:
const [students] = useState([...])
const [currentUser, setCurrentUser] = useState(undefined)
const [searchTerm, setSearchTerm] = useState(undefined)
const checkStudent = () => {
const match = students.find(student => student.admissionNumber === searchTerm)
if(match) {
setCurrentUser(match)
}
}
return (
<>
<button
onClick={() => checkStudent()}
/>
<input
type="text"
name="studentFullName"
className="form-control mt-2 offset-md-2 col-md-8"
placeholder="Student Name"
value={currentUser?.fullname}
/>
</>
)