I have a react form whose data must be saved in an array (what i want) values from the form include 3 selectable values, and two input values ( a number input and textarea ) each time a user is done with entering data, they press save button to push that entered data ( converted to an object ) into the array. I expect that each time user clicks save button, they are saving different data from the previous, which is then again pushed into the same array as an object. the array has a fixed length and I expect to not accept anymore data if it reaches a certain length. this works very well each a user saves data in that array only when the values of the two input fields (number input and textarea) the challenge is that each time the user data from any of the 3 selectable (as long as a select field is touched, even if the selected value is the same as the preivous value) values, the array loses the previous data and saves the current as array[0], the select fields are customised components using react-select, I extract data selected using useState, and other input fields are customised components from regular html input fields, I extract data input, using useRef, and forwadRef.
Kindly help and suggest how I can go about this.
export const Input = forwardRef((props, ref) => {
return (
<input
min={1}
required={props.required}
type={props.type}
id={props.id}
name={props.name}
placeholder={props.placeholder}
autoComplete={props.autoComplete}
autoCapitalize="none"
autoCorrect='off'
onChange={props.onChange}
defaultValue={props.defaultValue}
ref={ref}
{...props}
/>
);
});
const MySelect = (props) => {
const MyTheme = (theme) => {
return {
...theme,
colors: {
...theme.colors,
primary25: "#bdbdbd",
color: "#000",
primary: "rgba(0, 0, 0, 0.3)",
},
};
};
return (
<div style={{ width: "80%" }}>
<Select
defaultValue={props.defaultValue}
menuPlacement="auto"
menuPosition="auto"
placeholder={props.placeholder}
value={props.value}
onChange={props.onChange}
options={props.options}
isSearchable
isDisabled={props.isDisabled}
theme={MyTheme}
name={props.name}
id={props.id}
// ref={props.ref}
required = {props.required}
/>
</div>
);
};
export default MySelect;
const TextReminder = (props) => {
const [kickStart, setKickStart] = useState("");
const TimerRef = useRef();
const [units, setUnits] = useState("");
const [tag, setTag] = useState("");
const tagRef = useRef()
const TextRef = useRef();
const maxW = props.maxW
const handleAdd = (e) => {
e.preventDefault();
};
const Reminders = []
const handleSave = (e) => {
e.preventDefault();
const TextInput = TextRef.current.value;
const TimeInput = TimerRef.current.value;
const KickStart = kickStart.value;
const Units = units.value;
const Tag = tag.value;
const Wmax = maxW.current.value
const Reminder = {
Message: TextInput,
Starter: KickStart,
SendTime: TimeInput,
TimeUnits: Units,
Id: Tag
};
if (Reminders.length !== parseInt(Wmax)) {
Reminders.push(Reminder);
console.log(Reminders);
} else {
console.log("can not save any more Reminders");
}
};
const Remove = (e) => {
e.preventDefault();
if (Reminders.length > 0) {
Reminders.pop();
console.log(Reminders);
} else {
alert("Sorry, your sms reminders basket is now empty");
console.log(Reminders);
}
};
const handleSubmit = (e) => {
e.preventDefault();
};
return (
<div>
<form onSubmit={handleSubmit}>
<div className={classes.email}>
<div className={classes.split}>
<div className={classes.left}>
<div className={classes.lefthead}>settings</div>
<div className={classes.selectors}>
<div className={classes.time}>
<div className={classes.heads}>
when{" "}
<span
className={classes.add}
title="Click to start editing your reminders. one at a time"
>
<FButton onClick={handleAdd}>add</FButton>
</span>
</div>
<MySelect
placeholder="Select Time ..."
options={Determinants}
onChange={setKickStart}
/>
</div>
<div className={classes.trigger}>
<div className={classes.heads}>select trigger</div>
<div className={classes.tips}>
<div className={classes.tip1}>
<Input type="number" defaultValue="5" ref={TimerRef} />
</div>
<div className={classes.tip2}>
<MySelect
placeholder="Units ..."
options={RideTriggers.before}
onChange={setUnits}
/>
</div>
</div>
</div>
<div className={classes.tags}>
<div className={classes.heads}>select tag</div>
<MySelect
placeholder="Select Tag ..."
options={Tags}
onChange={setTag}
ref={tagRef}
/>
</div>
</div>
</div>
<div className={classes.right}>
<div className={classes.label}>
<div className={classes.a}>
<label htmlFor="body">Body</label>
</div>
<div className={classes.b}>
<FButton>+ variables</FButton>
</div>
</div>
<div className={classes.textarea}>
<TextArea id="body" className={classes.text} ref={TextRef} />
</div>
<div className={classes.bb}>
<div
className={classes.bbf}
title="Remove the immediate previously saved reminder from your basket"
>
<FButton onClick={Remove}>remove</FButton>
</div>
<div
className={classes.bbf}
title="Add this reminder to your basket in the order of first-saved is first-sent and last-saved is last-sent"
>
<FButton onClick={handleSave}>save</FButton>
</div>
</div>
</div>
</div>
<footer>
<div className={classes.footerbtn}>
<div className={classes.close}>
<FButton type={"submit"}>done</FButton>
</div>
{/* <div className={classes.close}>
<FButton>cancel</FButton>
</div> */}
</div>
</footer>
</div>
</form>
</div>
);
};
export default TextReminder;
Related
I am trying to fetch the user based on their id but can't sometimes I get the user but it is like the state is preserving the values if I try to get another user value but I still saw the previous user data. Hey anyone please help me I am stuck here for hours now.
here is the image of my users list
here is an update
I went to my network tab and saw that
I get the user Based on their users but I see the value in fields until I click on my first user in the shown list which is shown in fields then again If click the next user I still see the first user details so it definitely not API issue in my state problem please help me to fix this.
EditUser.tsx
const UpdateUser = ({ setUsers, id }) => {
const animatedComponents = makeAnimated();
const [rolesData, setRolesData] = useState([]);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [role, setRole] = useState("");
const [job_preference, setJobPreference] =
useState([]);
const [multiSelect, setMultiSelect] = useState(
[]
);
const fetchDataBasedOnID = async (id) => {
const { data } = await axios.get(
`/api/user/${id}`
);
getDataOfMultiSelect(data);
};
const getDataOfMultiSelect = (data) => {
const result = states.filter((state) =>
data.job_preference.includes(state.value)
);
setMultiSelect(result);
setName(data.name);
setEmail(data.email);
setRole(data.role);
};
const resetFields = () => {
setName("");
setEmail("");
setMultiSelect("");
setRole("");
};
useEffect(() => {
handleRoles();
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
const { data } = await axios.put(
`/api/user/${id}`,
{
name,
email,
role,
job_preference,
}
);
toast.success("User updated successfully");
usersList();
resetFields();
};
return (
<>
{/* Button trigger modal */}
<div>
<button
type='button'
onClick={() => fetchDataBasedOnID(id)}
className='btn btn-warning'
data-bs-toggle='modal'
data-bs-target='#updateModal'>
<BiEdit />
</button>
</div>
{/* Modal */}
<div
className='modal fade'
id='updateModal'
tabIndex='-1'
aria-hidden='true'>
<div className='modal-dialog modal-dialog-centered'>
<div className='modal-content'>
<div className='modal-header'>
<h1
className='modal-title fs-5'
id='exampleModalLabel'>
Update User
</h1>
<button
type='button'
className='btn-close'
data-bs-dismiss='modal'
aria-label='Close'></button>
</div>
<form onSubmit={handleSubmit}>
<div className='modal-body'>
<div className='mb-3'>
<label className='form-label'>
Name
</label>
<input
type='text'
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
className='form-control'
placeholder='John Doe'
/>
</div>
<div className='mb-3'>
<label className='form-label'>
Email address
</label>
<input
type='email'
value={email}
required
onChange={(e) =>
setEmail(e.target.value)
}
className='form-control'
placeholder='name#example.com'
/>
</div>
<div className='mb-3'>
<label className='form-label'>
Role
</label>
<select
className='form-select option'
value={role}
onChange={handleRoleChange}>
<option className='option'>
Select a Role
</option>
{rolesData?.map(
({ _id, role_name }) => (
<option
className='option'
key={_id}
value={role_name}>
{role_name}
</option>
)
)}
</select>
</div>
<div className='mb-3'>
<label className='form-label'>
Job Preference
</label>
<Select
value={multiSelect}
isMulti
components={
animatedComponents
}
isSearchable
placeholder='Choose any 3 states as job location'
options={states}
onChange={
handleMultiSelectChange
}
isOptionDisabled={() =>
multiSelect.length >= 3
}
/>
</div>
</div>
<div className='modal-footer'>
<button
type='button'
className='btn btn-secondary'
data-bs-dismiss='modal'>
Close
</button>
<button
type='submit'
className='btn btn-success'
data-bs-dismiss='modal'>
Save Changes
</button>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default UpdateUser;
I am trying to create a Form validation for 4 inputs, companyName - email - name - phone.
My aim is to validate them using regular expression and won't allow the user to press submit unless the inputs aregood to go.
Problem :
I set the logic testing just two validations so far, the company name and the email.
When I press sometimes submit, it ignores the validation and sometimes not.
Please help me to let them work properly.
import { React, useState, useEffect } from "react";
/*
*****
*****
Import Showcase.scss -
*/
import "./Showcase.scss";
const Showacase = () => {
/*
*****
*****
Logic Goes Here
*/
//State Management
//Company State
const [company, setCompany] = useState();
const [companyValue, setCompanyValue] = useState();
//name State
const [name, setName] = useState();
const [nameValue, setNameValue] = useState();
//Phone State
const [phone, setPhone] = useState();
const [phoneValue, setPhoneValue] = useState();
//Email State
const [email, setEmail] = useState();
const [emailValue, setEmailValue] = useState();
//Hide Inputs
const [input, setInput] = useState(true);
// Message State
const [message, setMessage] = useState(true);
// SubMessage
const [subMessage, setSubMessage] = useState(true);
//InsertLine
const [line, setLine] = useState(false);
//Hide Button
const [btn, setBtn] = useState(true);
/* Validation State */
const [emailValidate, setEmailValidate] = useState(false);
/* Validation State */
const [companyValidate, setCompanyValidate] = useState(false);
// OnSubmit
const onSubmit = (e) => {
e.preventDefault();
};
//onChange Company
const onChangeCompany = (e) => {
setCompany(e.target.value);
};
//OnClick
const onClick = (e) => {
// Company and Name
const reCompanyName = /^[a-zA-Z]{2,12}$/;
// Email Address
const reEmail =
/^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/;
if (!reCompanyName.test(company)) {
setCompanyValidate(true);
return;
}
if (!reEmail.test(email)) {
setEmailValidate(true);
return;
}
//upComing Phone
if (company === "" || phone === "" || email === "" || name === "") {
return;
} else {
/* setting the New Values */
setCompanyValue(company);
setNameValue(name);
setPhoneValue(phone);
setEmailValue(email);
/* Clearing the Inputs */
setCompany("");
setName("");
setPhone("");
setEmail("");
/* Logic of hidding Items */
setInput(false);
setMessage(false);
setSubMessage(false);
setLine(true);
setBtn(false);
/* Reset all options after submission after 5 seconds */
setTimeout(() => {
setInput(!false);
setMessage(!false);
setSubMessage(!false);
setLine(!true);
setBtn(!false);
}, 5000);
}
};
return (
<>
{/* Showcase Main */}
<div className="Showcase">
<div className="Container">
{/* Showcase Main / Applying the flex */}
<div className="Showcase__inside">
{/* Right Side / form and logic */}
<div className="Showcase__inside--left">
<div className="Showcase__inside--left--box">
{/* the Top Message */}
{message ? (
<h1>Find inbound call centers for your company</h1>
) : (
<h1>Thank you for your request!</h1>
)}
{/* the sub Message Message */}
{subMessage ? (
<p>
Use our AI and Big Data driven call center sourcing solution
</p>
) : (
<p className="paragraphAfterSubmission">
You’ve taken the first step. Our experts will get in touch
with you soon.
</p>
)}
{/* Inserting the Line */}
{line ? <hr></hr> : null}
<form onSubmit={onSubmit}>
{/* Company */}
<div className="Showcase__inside--left--box--form">
<label for="company">Company </label>
{input ? (
<div>
<input
required
value={company}
onChange={onChangeCompany}
type="text"
id="company"
placeholder="Company"
name="company"
/>
{companyValidate ? (
<div className="companyValidate">
<small>Please Provide a valid Company Name</small>
</div>
) : (
""
)}
</div>
) : (
<p>{companyValue}</p>
)}
</div>
{/* Name */}
<div className="Showcase__inside--left--box--form">
<label for="Name">Name </label>
{input ? (
<input
required
onChange={(e) => setName(e.target.value)}
value={name}
type="text"
id="Name"
placeholder="Full name"
name="Name"
/>
) : (
<p>{nameValue}</p>
)}
</div>
{/* Phone */}
<div className="Showcase__inside--left--box--form">
<label for="Phone">Phone </label>
{input ? (
<input
required
onChange={(e) => setPhone(e.target.value)}
value={phone}
type="text"
id="Phone"
placeholder="+49"
name="phone"
/>
) : (
<p>{phoneValue}</p>
)}
</div>
{/* Email */}
<div className="Showcase__inside--left--box--form">
<label for="Email">Email </label>
{input ? (
<div>
<input
required
onChange={(e) => setEmail(e.target.value)}
value={email}
type="email"
id="Email"
placeholder="name#mail.com"
name="email"
/>
{emailValidate ? (
<div className="EmailValidate">
<small>Please Provide a valid Email</small>
</div>
) : (
""
)}
</div>
) : (
<p>{emailValue}</p>
)}
</div>
{/* Submit */}
<div className="Showcase__inside--left--box--form">
<div className="Showcase__inside--left--box--form--submit">
{/* OnClick Method */}
{btn ? (
<button onClick={onClick} type="submit">
Get informed
</button>
) : null}
</div>
</div>
</form>
</div>
</div>
{/* Right SIDE %s */}
<div className="Showcase__inside--right">
<div>
<div>
<h1>Welcome to Europe’s largest call center database </h1>
</div>
<div className="Showcase__inside--right--per">
<div className="Showcase__inside--right--per--single">
<small>500+</small>
<h1>Tenders</h1>
</div>
<div className="Showcase__inside--right--per--single">
<small>200+</small>
<h1>Callcenter</h1>
</div>
<div className="Showcase__inside--right--per--single">
<small>375.000</small>
<h1>Agents available</h1>
</div>
<div className="Showcase__inside--right--per--single">
<small>85%</small>
<h1>Faster sourcing</h1>
</div>
</div>
</div>
</div>
{/* Right Ended %s */}
</div>
</div>
</div>
</>
);
};
export default Showacase;
I have a problem with the access of the input value, I used here is .map().
Here is the code, <QuestionLabel/>is the children component. projectsData.projectDetail is an available data
//..
{projectsData.projectDetail.questions.map((question) => (
<QuestionLabel
questionTitle={question}
/>
))}
//child component
const QuestionLabel=(props)=>{
const [answerInput, setAnswerInput] = React.useState("");
return(
<div className="contact">
<form className="contact-form" autocomplete="off">
<div class="contact-form-group">
<label
for="name"
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id="name"
type="text"
class="contact-form-input"
value={answerInput}
onChange={(answer) => setAnswerInput(answer.target.value)}
/>
</div>
</form>
</div>
);
}
export default QuestionLabel;
There are many way to get value from child component for parent component. You can call a function pass from parent to children to set value for parent state when it's changed. Exmaple:
const ParentComponent =(props)=>{
const [valueFromChild, setValueFromChild] = useState('');
return <>
{valueFromChild}
<QuestionLabel questionTitle={'Title'} setValueFromChild={setValueFromChild}/>
</>
}
const QuestionLabel=(props)=>{
const [answerInput, setAnswerInput] = React.useState("")
useEffect(() => {
props.setValueFromChild(answerInput);
}, [answerInput]);
return(
<div className="contact">
<form className="contact-form" autoComplete="off">
<div class="contact-form-group">
<label
for="name"
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id="name"
type="text"
class="contact-form-input"
value={answerInput}
onChange={(answer) => setAnswerInput(answer.target.value)}
/>
</div>
</form>
</div>
);
}
So you need to have the input state up in the parent.
Since you are mapping through questions array to render the QuestionLabel we can try this...
//..
const ParentComponent = () => {
const [answers, setAnswers] = React.useState({})
useEffect(() => {
projectsData.projectDetail.questions.forEach((_, i) => {
setAnswers(previousAnswers => {...previousAnswers, ["name" + i]: ""})
})
}, [])
const handleAnswers = (e) => {
setAnswers(previousAnswers => {...previousAnswers, [e.target.name]: [e.target.value]})
}
//..
then
//..
{
projectsData.projectDetail.questions.map((question, i) => {
return (
<QuestionLabel questionTitle={question} inputName={"name" + i} answers={answers} handleAnswers={handleAnswers} />
)
})
}
and finally...
//child component
const QuestionLabel=(props)=>{
return(
<div className="contact">
<form className="contact-form" autocomplete="off">
<div class="contact-form-group">
<label
for={props.inputName}
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id={props.inputName}
name={props.inputName}
type="text"
class="contact-form-input"
value={answers[props.inputName]}
onChange={handleAnswers}
/>
</div>
</form>
</div>
);
}
export default QuestionLabel;
I Am populating values of my input field from JSON data what am getting from back-end, now there is an edit button on UI by on click on that button I am enabling my input field but not able to type inside as I am setting some value
I want to write inside the input once I have made them editable.
const { register, handleSubmit, errors } = useForm();
const [disabled, setdisabled] = useState(false);
const [editBtn, seteditBtn] = useState(true);
<form onSubmit={handleSubmit(onSubmit)}>
{editBtn === true && (
<div align="right">
<button
className="btn white_color_btn"
type="button"
onClick={edit}
>
Edit
</button>
</div>
)}
{editBtn === false && (
<button className="btn white_color_btn" type="submit">
Save
</button>
)}
<div className="row">
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
disable
id="firstName"
name="firstName"
value={dataItems.firstname}
disabled={disabled ? "" : "disabled"}
ref={register({ required: true })}
/>
{errors.firstname && (
<span className="text-danger">first name required</span>
)}
<br />
<label htmlFor="emp_designation">First name</label>
</div>
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
disabled
id="lastname"
name="lastname"
value={dataItems.lastname}
disabled={disabled ? "" : "disabled"}
ref={register({ required: true })}
/>
{errors.lastname && (
<span className="text-danger">last name required</span>
)}
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</form>
On click of edit
const edit = () => {
setdisabled(true);
};
Code sandbox
You need to make your input as a controlled component and write onChange handlers which will update the state. This will allow you to edit the input field values. Demo
const [disabled, setdisabled] = useState(false);
const [name, setName] = useState(empData.item.name) // setting default name
const [lastname, setLastname] = useState(empData.item.lastname) // default lastname
const edit = () => {
setdisabled(true);
};
return (<div className="container-fluid">
<div align="right">
<button className="btn" onClick={edit}>
Edit
</button>
</div>
<div className="row">
<div>
<input
type="text"
disable
id="item.value"
value={name}
onChange={(e) => {
setName(e.target.value)
}}
disabled={disabled ? "" : "disabled"}
/>
<br />
<label htmlFor="name">Name</label>
</div>
<div>
<input
type="text"
disabled
id={"lastname"}
value={lastname}
onChange={(e) => {
setLastname(e.target.value)
}}
disabled={disabled ? "" : "disabled"}
/>
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</div>);
Your input is controlled by the value you are giving to it. ie: Its value is always for example empData.item.name.
And you are not providing a change handler to handle the change.
Try adding something like this:
function myChangeHandler(e){
setEditedValueSomeHow(e.target.value);
}
<input
// ...
onChange={myChangeHandler}
/>
Read more about uncontrolled components
PS: you should have had a warning message in your console like this one:
Edit:
You are using react-hook-form to manage your form but at the same time giving values to your inputs.
Please refer to this link to initialize your form values.
short story:
Remove value form your input.
Pass an object to useForm hook containing initial values.
const { register, handleSubmit, errors } = useForm({
defaultValues: {
firstName: "steve",
lastname: "smith"
}
});
Here is a working fork for your codesandbox
In order to make the input editable, you need to update a local state which controlls the input value. As suggested by you in the comments, you are using graphql to get the data, you can make use of useEffect to set the data in state and then on click of edit, update the localState
export default function App() {
const { register, handleSubmit, errors } = useForm();
const [disabled, setdisabled] = useState(true);
const [editBtn, seteditBtn] = useState(true);
const { loading, data } = useQuery("some qraphql query here"); // getting data from graphql
const [formData, setFormData] = useState({});
useEffect(() => {
setFormData(data);
}, [data]);
const edit = () => {
setdisabled(false);
seteditBtn(false);
};
const onSubmit = () => {
console.log(formData);
// submit data using formData state.
};
const handleChange = e => {
const name = e.target.name;
const value = e.target.value;
console.log(name, value);
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<div className="container-fluid">
<form onSubmit={handleSubmit(onSubmit)}>
{editBtn === true && (
<div align="right">
<button
className="btn white_color_btn"
type="button"
onClick={edit}
>
Edit
</button>
</div>
)}
{editBtn === false && (
<button className="btn white_color_btn" type="submit">
Save
</button>
)}
<div className="row">
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
id="firstname"
name="firstname"
onChange={handleChange}
value={formData.firstname}
disabled={disabled}
ref={register({ required: true })}
/>
{errors.firstname && (
<span className="text-danger">first name required</span>
)}
<br />
<label htmlFor="emp_designation">First name</label>
</div>
<div className="form-group col-6 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<input
type="text"
id="lastname"
name="lastname"
value={formData.lastname}
onChange={handleChange}
disabled={disabled}
ref={register({ required: true })}
/>
{errors.lastname && (
<span className="text-danger">last name required</span>
)}
<br />
<label htmlFor="lastname">Lastname</label>
</div>
</div>
</form>
</div>
);
}
Working mock demo
I have a dialog window (div) with a form in it and an object which holds the data from the fields, if the user forgets to fill one of the fields the border of this field becomes red. My problem is that if the user closes the dialog window and reopens it the red borders are still there and the object holds information from before the closing. I am not sure how/if I need to clear the local storage or if there is a better solution so every time the window is closed the data structure and styles are defaulted. I use Bootstrap for the components.
import React from 'react';
const variables = {
varA: '', varB: '', varC: ''
};
let emptyFields = [];
const border = {
borderColor: '#C70039',
};
const submitForm = () => {
emptyFields = [];
if (Object.values(variables).includes('')) {
for (const [key, value] of Object.entries(variables)) {
if (value === '') {
emptyFields.push(key);
}
}
}
return emptyFields;
};
const Form = empty => (
<div>
<p className="text-primary">Please provide information in all of the fields.</p>
<form>
<div className="form-group mt-4" >
<label htmlFor="formGroupExampleInput">Var A </label>
<input type="text" className='form-control' id="varA" required onChange={(e) => { variables.varA = e.target.value; } } />
</div>
<div className="form-group">
<label htmlFor="formGroupExampleInput">Var B </label>
<input type="text" id="varB" className='form-control' style={empty.empty.includes('varB') ? border : null} onChange={(e) => { variables.varB = e.target.value; }}/>
</div>
<div className="form-group">
<label htmlFor="formGroupExampleInput">Var C </label>
<input type="text" className="form-control" id="varC" style={empty.empty.includes('varC') ? border : null} onChange={(e) => { variables.varC = e.target.value; }}/>
</div>
</form>
<button className="btn btn-primary" onClick={submitForm}>Submit</button>
</div>
);
const CompleteForm = () => <Form empty = {emptyFields} />;
export default CompleteForm;