Reactjs Dynamic Form Getting Error OnChnage input value? - javascript

I am created a dynamic form in react.js but i can not type anything value in input because onchnage function not working i don't know why i tried a lot of times but i am getting failed and adding form and deleting form is working all right only input value not working here is my code and codesandbox link https://codesandbox.io/s/reactdynamicform-02cho .
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputFields: [
{
firstName: "",
lastName: ""
}
]
};
}
handleAddFields = () => {
const values = this.state.inputFields;
values.push({ firstName: "", lastName: "" });
this.setState({
values
});
};
handleRemoveFields = index => {
const values = this.state.inputFields;
values.splice(index, 1);
this.setState({
values
});
};
async onChange(e, index) {
if (
["firstName","lastName"].includes(e.target.name)
) {
let cats = [...this.state.inputFields];
cats[index][e.target.name] = e.target.value;
await this.setState({
cats
});
}else{
this.setState({ [e.target.name]: e.target.value.toUpperCase() });
}
console.log(this.state.inputFields);
}
handleSubmit = e => {
e.preventDefault();
console.log("inputFields", this.state.inputFields);
};
render() {
return (
<>
<h1>Dynamic Form Fields in React</h1>
<form onSubmit={this.handleSubmit.bind(this)}>
<div className="form-row">
{this.state.inputFields.map((inputField, index) => (
<div key={`${inputField}~${index}`}>
<div className="form-group col-sm-6">
<label htmlFor="firstName">First Name</label>
<input
type="text"
className="form-control"
id="firstName"
name="firstName"
value={inputField.firstName}
onChange={this.onChange.bind(index)}
/>
</div>
<div className="form-group col-sm-4">
<label htmlFor="lastName">Last Name</label>
<input
type="text"
className="form-control"
id="lastName"
name="lastName"
value={inputField.lastName}
onChange={this.onChange.bind(index)}
/>
</div>
<div className="form-group col-sm-2">
<button
className="btn btn-link"
type="button"
onClick={() => this.handleRemoveFields(index)}
>
-
</button>
<button
className="btn btn-link"
type="button"
onClick={() => this.handleAddFields()}
>
+
</button>
</div>
</div>
))}
</div>
<div className="submit-button">
<button
className="btn btn-primary mr-2"
type="submit"
// onSubmit={this.handleSubmit}
>
Save
</button>
</div>
<br />
<pre>{JSON.stringify(this.state.inputFields, null, 2)}</pre>
</form>
</>
);
}
}
export default App;

You approach is not the correct. Use object to contain form values
state = {
inputFields: { firstName: '', lastName: '' }
}
onChange = (e) => {
const { name, value } = e.target;
this.setState(prevState => ({ inputFields: { ...prevState.inputFields, [name]: value } }));
}
// in jsx
<input name="firstName" onChange={this.onChange} />

try this
onChange={(e)=>{this.onChange(e, index)}}
instead of
onChange={this.onChange.bind(index)}

1) Since your inputFields state is an array, you can't just call this.state.inputFields.firstName and even less inputField.firstName.
You have to call this.state.inputsFields[0].firstName.
2) If you want the index AND the event, you have to pass the onChange event like this :
<input
type="text"
className="form-control"
id="lastName"
name="lastName"
onChange={event => this.handleChange(event, index)}
/>
handleChange = (event, index) => {
console.log(event.currentTarget.value, index);
};
// output : {whatever you type} {index of the form}
// exemple : "hello 1"

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"
/>

Adding an item to react state dictionary

I want to add an item to a react state dictionary. Every time i press to submit i got the value is undefined on the dictionary object and it is append to the dictonary with a null values in name and cost but id is working fine. I find the issue but i am unable to find the solution. TIA.
import React, { Component } from "react";
import ExpenseItem from "./ExpenseItem.js";
class ExpensesList extends Component {
state = {
expenses: [{id: '12345' ,name: 'Pizza', cost: '20'}],
};
handleChange=(event)=>{
this.setState({[event.target.name]:event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
this.setState({expenses: [this.state.expenses,...[{id: Math.random(),name:this.state.expenses.name, cost:this.state.expense.cost}]]});
}
render() {
return (
<div>
<ul className="list-group">
{this.state.expenses.map((expense) => (
<ExpenseItem
id={expense.id}
name={expense.name}
cost={expense.cost}
/>
))}
</ul>
<div className ="row mt-3">
<h2> Add Expenses </h2>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="form-group">
<label for="name">Name</label>
<input
required="required"
type="text"
className="form-control"
id="name"
value = {this.state.expenses.name}
onChange = {this.handleChange}
></input>
</div>
<div className="form-group">
<label for="name">Cost</label>
<input
required="required"
type="text"
className="form-control"
id="cost"
value = {this.state.expenses.cost}
onChange = {this.handleChange}
></input>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"> Add Expense</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
export default ExpensesList;
The issues is that this.state.expenses is an array. So, this.state.expenses.name and this.state.expenses.cost are both undefined.
If you want to be able to add new expense objects to the expenses array in state, you need a way to manage the new inputs in state. So, your state should look something like this:
state = {
expenses: [{id: '12345' ,name: 'Pizza', cost: '20'}],
cost: "",
name: ""
};
When a user inputs a cost and name, this.state.cost and this.state.name should be set in state, and then when the user clicks 'submit', a new object can be added to the this.state.expenses array.
(Also, your handleChange needs to specify which properties of state it intends to udpate)
Your final solution should look something like this:
import React, { Component } from "react";
import ExpenseItem from "./ExpenseItem.js";
class ExpensesList extends Component {
state = {
expenses: [{ id: '12345', name: 'Pizza', cost: '20' }],
cost: "",
name: ""
};
handleChange = (event, name) => {
this.setState({ [name]: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
let newExpense = {
id: Math.random(),
name: this.state.name,
cost: this.state.cost
}
this.setState(prevState => ({
expenses: [...prevState.expenses, newExpense], // add new expense to expenses array
cost: "", // reset this field
name: "" // reset this field
}));
}
render() {
return (
<div>
<ul className="list-group">
{this.state.expenses.map((expense) => (
<ExpenseItem
id={expense.id}
name={expense.name}
cost={expense.cost}
/>
))}
</ul>
<div className="row mt-3">
<h2> Add Expenses </h2>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="form-group">
<label for="name">Name</label>
<input
required="required"
type="text"
className="form-control"
id="name"
value={this.state.name}
onChange={e => this.handleChange("name")}
></input>
</div>
<div className="form-group">
<label for="name">Cost</label>
<input
required="required"
type="text"
className="form-control"
id="cost"
value={this.state.cost}
onChange={e => this.handleChange("cost")}
></input>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"> Add Expense</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
export default ExpensesList;

How to update state if I have props coming from parent component in React Hooks

I am trying to get updated textbox value if I have props from parent component.
Here is my code -
const initialState = {
name: "",
age: 0
}
const UserAdd = (props:any) => {
const {selectedUser} = props;
const [state, setState] = useState<any>(initialState);
const dispatch = useDispatch();
const onChangeValue = (event:any) => {
const { name, value } = event.target;
setState((prevState:any) => (
{ ...prevState, [name]: value }
));
}
const onSubmit = (e:any) => {
e.preventDefault();
const { name } = e.target;
dispatch(addUser(state.name, state.age))
setState({ ...initialState });
}
const onUpdate = (e:any) => {
e.preventDefault();
const { name } = e.target;
console.log(state.name , state.age , "name-age")
}
return (
<div className="add-user">
<hr />
<h2>Add User</h2>
<hr />
{ selectedUser ?
(<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={selectedUser.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={selectedUser.age} />
<button type="submit" onClick={onUpdate} className="btn btn-primary col-md-2 mb-2">Update</button>
</form>)
:
(
<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={state.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={state.age} />
<button type="submit" onClick={onSubmit} className="btn btn-primary col-md-2 mb-2">Submit</button>
</form>
)
}
</div>
)
}
export default UserAdd;
When There is no props i.e (no selectedUser), Then state change working fine and I am able to dispatch action as well. But When Props(selectedUser) is available, then I am unable to edit textbox field & unable to get updated state. Please someone help me.
look deeper into your code.. there is different forms when selectedUser is not falsy...
when there is selected user:
value={selectedUser.name}
shouled be like the other form's input:
value={state.name}
u also need to add useEffect to change the state when the prop is being changed, u can do it like so:
useEffect(()=>{
setState((prevState:any) => (
{ ...prevState, name: selectedUser.name}
));
},[selectedUser.name])
the useEffect then will be executed whenever any item of the dependency list will be different from the last render (in this case each time the selectedUser.name is being changed)
In case there is selectedUser, the value attribute of input field should be reactive. Currently, it is set to props that won't react to onChangeValue handler. Use state in the value attribute (state.name /state.age) and initialize states with selectedUser props value. It might look like below -
const initialState = {
name: "",
age: 0
}
const UserAdd = (props:any) => {
const {selectedUser} = props;
//Button text would be Update or submit that depends on selectedUser.
const btnText = selectedUser ? "Update" : "Submit";
//If it founds selected user then set to it otherwise go with initialState
const [state, setState] = useState<any>(selectedUser || initialState);
const dispatch = useDispatch();
const onChangeValue = (event:any) => {
const { name, value } = event.target;
setState((prevState:any) => (
{ ...prevState, [name]: value }
));
}
const onSubmit = (e:any) => {
e.preventDefault();
const { name } = e.target;
dispatch(addUser(state.name, state.age))
setState({ ...initialState });
}
return (
<div className="add-user">
<hr />
<h2>Add User</h2>
<hr />
<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={selectedUser.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={selectedUser.age} />
<button type="submit" onClick={onUpdate} className="btn btn-primary col-md-2 mb-2">{btnText}</button>
</form>
</div>
)
}
export default UserAdd;
Once you set it up like this you don't need to render forms conditionally. you only need one form. I have added a few comments as well. I have created a working POC here https://codesandbox.io/s/propstostate-react-ssirn

How to set cursor in the input field once i clear data-reactjs

I am trying to clear the input field and make the cursor to focus on the same input field. I tried using .focus() and autofocus function, but still I am not able to get the values.
resetInput = () => {
this.setState({ search: '' });
}
render() {
return (
<div className="storyboard-search-box clearfix">
<input type="text" placeholder="Search..." value={this.state.search} onChange={this.searchBoard} />
<button className="clear-search-button">
<img src={clearIcon} alt="clear button" title="Clear All" onClick={this.resetInput}/>
</button>
</div>
);
}
You can store the ref of the input, and call the focus method on that.
Example
class App extends React.Component {
state = {
search: ""
};
searchBoard = e => {
this.setState({ search: e.target.value });
};
resetInput = () => {
this.setState({ search: "" });
this.ref.focus();
};
componentDidMount() {
setTimeout(this.resetInput, 2000);
}
render() {
return (
<div>
<input
type="text"
placeholder="Search..."
value={this.state.search}
onChange={this.searchBoard}
ref={ref => (this.ref = ref)}
/>
</div>
);
}
}
You can achieve this with ref callback in React.
resetInput = () => {
this.setState({ search: '' });
if (this.textInput) this.textInput.focus();
}
<div className="storyboard-search-box clearfix">
<input ref={ref => { this.textInput = ref }} type="text" placeholder="Search..." value={this.state.search} onChange={this.searchBoard} />
<button className="clear-search-button">
<img src={clearIcon} alt="clear button" title="Clear All" onClick={this.resetInput}/>
</button>
</div>

Redux form defaultValue

How to set defaultValue to input component?
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
I tried like above but my fields are empty. I'm trying to create fieldArray (dynamic forms):
{fields.map((prize, index) =>
<div key={index} className="fieldArray-container relative border-bottom" style={{paddingTop: 35}}>
<small className="fieldArray-title marginBottom20">Prize {index + 1}
<button
type="button"
title="Remove prize"
className="btn btn-link absolute-link right"
onClick={() => fields.remove(index)}>Delete</button>
</small>
<div className="row">
<div className="col-xs-12 col-sm-6">
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
<Field name={`${prize}.prizeId`} defaultValue={index} component={Input} type='text'/>
<Field
name={`${prize}.name`}
type="text"
component={Input}
label='Prize Name'/>
</div>
<div className="col-xs-12 col-sm-6">
<Field
name={`${prize}.url`}
type="text"
component={Input}
label="Prize URL"/>
</div>
<div className="col-xs-12">
<Field
name={`${prize}.description`}
type="text"
component={Input}
label="Prize Description" />
</div>
</div>
</div>
)}
On redux forms you can call initialize() with an object of values like so:
class MyForm extends Component {
componentWillMount () {
this.props.initialize({ name: 'your name' });
}
//if your data can be updated
componentWillReceiveProps (nextProps) {
if (/* nextProps changed in a way to reset default values */) {
this.props.destroy();
this.props.initialize({…});
}
}
render () {
return (
<form>
<Field name="name" component="…" />
</form>
);
}
}
export default reduxForm({})(MyForm);
This way you can update the default values over and over again, but if you just need to do it at the first time you can:
export default reduxForm({values: {…}})(MyForm);
This jsfiddle has an example
https://jsfiddle.net/bmv437/75rh036o/
const renderMembers = ({ fields }) => (
<div>
<h2>
Members
</h2>
<button onClick={() => fields.push({})}>
add
</button>
<br />
{fields.map((field, idx) => (
<div className="member" key={idx}>
First Name
<Field name={`${field}.firstName`} component="input" type="text" />
<br />
Last Name
<Field name={`${field}.lastName`} component="input" type="text" />
<br />
<button onClick={() => fields.remove(idx)}>
remove
</button>
<br />
</div>
))}
</div>
);
const Form = () => (
<FieldArray name="members" component={renderMembers} />
);
const MyForm = reduxForm({
form: "foo",
initialValues: {
members: [{
firstName: "myFirstName"
}]
}
})(Form);
this is my implementation using a HoC
import { Component } from 'react'
import {
change,
} from 'redux-form'
class ReduxFormInputContainer extends Component{
componentDidMount(){
const {
initialValue,
meta,
} = this.props
if(initialValue === undefined || meta.initial !== undefined || meta.dirty) return
const {
meta: { form, dispatch },
input: { name },
} = this.props
dispatch(change(form, name, initialValue))
}
render(){
const {
initialValue,
component: Compo,
...fieldProps
} = this.props
return <Compo {...fieldProps} />
}
}
function reduxFormInputContainer(component){
return function(props){
return <ReduxFormInputContainer {...props} component={component} />
}
}
export default reduxFormInputContainer
and then for exemple:
import reduxFormInputContainer from 'app/lib/reduxFormInputContainer'
InputNumericWidget = reduxFormInputContainer(InputNumericWidget)
class InputNumeric extends Component{
render(){
const props = this.props
return (
<Field component={InputNumericWidget} {...props} />
)
}
}

Categories