Field array with one field at initial - javascript

I am trying to use the concept of field array because in my app a user should be able to fill multiple value for same kind of field. I could do as per redux-form field array example, however, in an example there is no initial field we have to first click on add member and then only we can have the field to fill the value but i need one field initially and then add further field if user wants to.
I tried from the redux-form example but its not allowing me to input the value at all. Here is the demo
https://codesandbox.io/s/n47qr2pvzl
The format of that field value should be like this
general: {
general_information: {
members: [
{
name: ''
},
{
name: ''
}
]
}
}
Here is the code
const renderMembers = ({ fields, meta: { touched, error, submitFailed } }) => (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>
Add Member
</button>
{(touched || submitFailed) && error && <span>{error}</span>}
</li>
{fields.map((member, index) => (
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}
/>
<h4>Member #{index + 1}</h4>
<Field
name={`${member}.name`}
type="text"
component={renderField}
label="Name"
/>
</li>
))}
</ul>
);
const FieldArraysForm = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<Field
name="general.general_information.clubName"
type="text"
component={renderField}
label="Club Name"
/>
<Field
name="general.general_information.members.name"
type="text"
component={renderField}
label="Name"
/>
<FieldArray
name="general.general_information.members"
component={renderMembers}
/>
<div>
<button type="submit" disabled={submitting}>
Submit
</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
);
};

According to Redux-Form, we can set the initial fields by specifying it like (in your case):
export default reduxForm({
form: "fieldArrays", // a unique identifier for this form
validate,
initialValues: {"general": {
"general_information": {
"members": [
{}
]
}
}
}
})(FieldArraysForm);
Here is the live demo
Hope it helps :)

Related

How to pre-populate fields on click of a button in react?

I am trying to pre-populate the form fields, that are replicated, from the fields that are already filled. On clicking the "Add fields" button, the fields are getting replicated. But I want them to get pre-populated using the data filled in the already existing fields.
From where can I get hold of the input values?
import './style.css';
export default function App() {
const [inputFields, setInputFields] = useState([{ name: '', age: '' }]);
const addFields = (e) => {
e.preventDefault();
let newField = { name: "", age: '' };
setInputFields([...inputFields, newField]);
};
const handleFormChange = (index, e) => {
let data=[...inputFields];
data[index][e.target.name]=[e.target.value];
setInputFields(data);
}
return (
<div>
<form>
{inputFields.map((input, index) => {
return (
<div key={index}>
<input
type="text"
name="name"
placeholder="Enter name"
value={input.name}
onChange={(e)=>handleFormChange(index, e)}
/>
<input
type="number"
name="age"
placeholder="Enter Age"
value={input.age}
onChange={(e)=>handleFormChange(index, e)}
/>
<br />
<br />
</div>
);
})}
<button onClick={addFields}>Add Field</button>
<br />
</form>
</div>
);
}```
You will need to track changes in input with an onChange handler.
Also, you are not setting values from the last input fields anywhere to be able to duplicate them. The below code might work as you expect:
const { useState } = React;
function App() {
const [inputFields, setInputFields] = useState([{ name: '', age: '' }]);
const addFields = (e) => {
e.preventDefault();
const temp = inputFields.slice()
, length = temp.length - 1
, { name, age } = temp[length]
// Set value from last input into the new field
let newField = { name, age }
setInputFields([...temp, newField])
}
, handleChange = (index, event) => {
const temp = inputFields.slice() // Make a copy of the input array first.
inputFields[index][event.target.name] = event.target.value // Update it with the modified values.
setInputFields(temp) // Update the state.
};
return (
<div>
<form>
{inputFields.map((input, index) => {
return (
<div key={index}>
<input
onChange={e => handleChange(index, e)}
value={input.name}
type="text"
name="name"
placeholder="Enter name"
/>
<input
onChange={e => handleChange(index, e)}
value={input.age}
type="number"
name="age"
placeholder="Enter Age"
/>
<br />
<br />
</div>
);
})}
<button onClick={addFields}>Add Field</button>
<br />
</form>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
You have to set the value property on the input field to populate, e.g:
<input
value={input.name}
type="text"
name="name"
placeholder="Enter name"
/>

i want to disabled button if textbox null in react

return (
{jobstate.jobs.map((data,i) =>{
<form>
<input type="text" placeholder="Post a comment" onChange={(e) => jobcmthandler(e,data._id,i) } />
<button type="button" onClick={postcmt} >Send</button>
</form>
})}
)
I generate dynamic HTML using the map function and I want to disabled button if text null for induvial form and also how to get text value on button click in react js
I can't really see why you'd want to do this but here you go (example):
import React, { useState } from "react";
export default function App() {
return ["Name", "Age"].map((label) => <Form label={label} />);
}
function Form({ label }) {
const [readValue, writeValue] = useState("");
return (
<form>
<label>{label}</label>
<input
type="text"
placeholder="Post a comment"
onChange={(e) => writeValue(e.target.value)}
value={readValue}
/>
<button
type="button"
onClick={() => console.log("Submit")}
disabled={readValue === ""}
>
Send
</button>
</form>
);
}

Formik Reset callback creates recursive issue

I have a very simple formik setup where I need to pass the new initial values when users press reset form button. I am following doc but I end up creating recursive issue.
formReset() is passed to formik as a param of onReset. The function is called but I am not sure where is the recursion happening.
Here is a codesandbox for your convenient. Change form value then try to reset the form.
App.js
// Helper styles for demo
import "./helper.css";
import { MoreResources, DisplayFormikState } from "./helper";
import React from "react";
import { render } from "react-dom";
import { Formik } from "formik";
import * as Yup from "yup";
const formReset = (_, {resetForm}) => {
resetForm({email: ''});
}
const App = () => (
<div className="app">
<h1>
Basic{" "}
<a
href="https://github.com/jaredpalmer/formik"
target="_blank"
rel="noopener noreferrer"
>
Formik
</a>{" "}
Demo
</h1>
<Formik
initialValues={{ email: "populate#test.com" }}
onSubmit={async values => {
await new Promise(resolve => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
onReset={formReset}
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required("Required")
})}
>
{props => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email" style={{ display: "block" }}>
Email
</label>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={
errors.email && touched.email
? "text-input error"
: "text-input"
}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
<MoreResources />
</div>
);
render(<App />, document.getElementById("root"));
Edit:
So... a better option would be to use initialValues in useState and pass enableReinitialize and change the state to "reset" the form. It's more easy than trying to use resetForm.
You don't need to pass a function to onReset and call resetForm, you can do that by just pass the type reset to the button and have the Form component instead of normal html form tag.
The Form component will handle the handleReset that will be trigger when you have a button with type="reset".
<Form>
{/* other components */}
<button
type="reset"
className="outline"
disabled={!dirty || isSubmitting}
>
Reset
</button>
</Form>
Here is a working example.

How to write inside input after making it editable?

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

React Redux Form: form is submiting with old values

I have a FieldArray in Redux Form that I push objects inside this array and right after that I have a callback to trigger a function to make a POST request.
When I submit the form I get the old values, because the push() method of the Redux Form is an async dispach from redux.
// Parent component
<FieldArray
name="myObjects"
component={ChildComponent}
onSubmitObjects={this.onSubmit} />
// Function
onSubmit = async () => {
const { getFormValues } = this.props;
const data = {
myObjects: getFormValues.myObjects
}
try {
// const contact = await Service.updateUser(data);
} catch (e) {
console.log(e)
}
}
I need to submit the form with the new values added in the array right after the push method.
// Function inside ChildComponent
addNewObject = () => {
const { fields, onSubmitObjects} = this.props;
fields.push({
number: 1,
name: 'Foo',
});
if (onSubmitObjects) {
onSubmitObjects(); // cb() to trigger a function in the parent component
}
}
Is there a way to call the callback with the new values right after the push method?
You should use a form with redux-form handleSubmit to wrap your FieldArray. You can optionally pass your custom submit function (to make API requests, submit validation, etc.) to handleSubmit so it'd look like this <form onSubmit={handleSubmit(this.onSubmit)}> ...
See this example from redux-form official docs:
FieldArraysForm.js
import React from 'react'
import {Field, FieldArray, reduxForm} from 'redux-form'
import validate from './validate'
const renderField = ({input, label, type, meta: {touched, error}}) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderHobbies = ({fields, meta: {error}}) => (
<ul>
<li>
<button type="button" onClick={() => fields.push()}>Add Hobby</button>
</li>
{fields.map((hobby, index) => (
<li key={index}>
<button
type="button"
title="Remove Hobby"
onClick={() => fields.remove(index)}
/>
<Field
name={hobby}
type="text"
component={renderField}
label={`Hobby #${index + 1}`}
/>
</li>
))}
{error && <li className="error">{error}</li>}
</ul>
)
const renderMembers = ({fields, meta: {error, submitFailed}}) => (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>Add Member</button>
{submitFailed && error && <span>{error}</span>}
</li>
{fields.map((member, index) => (
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}
/>
<h4>Member #{index + 1}</h4>
<Field
name={`${member}.firstName`}
type="text"
component={renderField}
label="First Name"
/>
<Field
name={`${member}.lastName`}
type="text"
component={renderField}
label="Last Name"
/>
<FieldArray name={`${member}.hobbies`} component={renderHobbies} />
</li>
))}
</ul>
)
const FieldArraysForm = props => {
const {handleSubmit, pristine, reset, submitting} = props
return (
<form onSubmit={handleSubmit}>
<Field
name="clubName"
type="text"
component={renderField}
label="Club Name"
/>
<FieldArray name="members" component={renderMembers} />
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
)
}
export default reduxForm({
form: 'fieldArrays', // a unique identifier for this form
})(FieldArraysForm)

Categories