React Material UI Textfield onChange - javascript

const [formData, setFormData] = useState({});
useEffect(() => {
fetch("/formdata")
.then((res) => res.json())
.then((data) => setFormData(data));
}, []);
console.log("Form Data", formData);
//Sorting by order
let attr;
form.forms.map((y) => {
return (attr = y.formAttributes.sort((a, b) => {
return a.order < b.order ? -1 : 1;
}));
});
return (
{attr.map((attri, index) => {
return (
<TextField
key={index}
label={attri.label}
value={formData[attri.datakey] || ""}
onChange={event => {const {value} = event.target; setFormData({formData: value})}}
/>
);
})}
)
I would like to ask help on how to manage multiple fields in Textfield onChange area? Currently, if I am going to input a value there are no changes that is happening.
Here's my code.
Tried the approach of using e.target.value however it would still stay the same.

You should add a name attribute, where the 'name' is the key of the key-value-pair in your object.
e.g.
<TextField
key={index}
name="somename"
label={attri.label}
value={formData[attri.datakey] || ""}
onChange={handleChange}
/>
Then you can update the field like that.
setFormData({
...formData,
[e.target.name]: e.target.value // becomes "somename: some value"
})

Related

Save Form values in ReactJS using checkboxes

I created a form component using react hook forms. The component is composed from a group of checkboxes and a text input. The text input appears when user click on the last checkbox custom. The idea of this one is: when the user will click on it appears a text input and the user can add a custom answer/option. Ex: if user type test within the input then when the user will save the form, there should appear in an array test value, but custom text should't be in the array. In my application i don't have access to const onSubmit = (data) => console.log(data, "submit");, so i need to change the values within Component component. Now when i click on submit i get in the final array the custom value. Question: how to fix the issue described above?
const ITEMS = [
{ id: "one", value: 1 },
{ id: "two", value: 2 },
{ id: "Custom Value", value: "custom" }
];
export default function App() {
const name = "group";
const methods = useForm();
const onSubmit = (data) => console.log(data, "submit");
return (
<div className="App">
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<Component ITEMS={ITEMS} name={name} />
<input type="submit" />
</form>
</FormProvider>
</div>
);
}
export const Component = ({ name, ITEMS }) => {
const { control, getValues } = useFormContext();
const [state, setState] = useState(false);
const handleCheck = (val) => {
const { [name]: ids } = getValues();
const response = ids?.includes(val)
? ids?.filter((id) => id !== val)
: [...(ids ?? []), val];
return response;
};
return (
<Controller
name={name}
control={control}
render={({ field, formState }) => {
return (
<>
{ITEMS.map((item, index) => {
return (
<>
<label>
{item.id}
<input
type="checkbox"
name={`${name}[${index}]`}
onChange={(e) => {
field.onChange(handleCheck(e.target.value));
if (index === ITEMS.length - 1) {
setState(e.target.checked);
}
}}
value={item.value}
/>
</label>
{state && index === ITEMS.length - 1 && (
<input
{...control.register(`${name}[${index}]`)}
type="text"
/>
)}
</>
);
})}
</>
);
}}
/>
);
};
demo: https://codesandbox.io/s/winter-brook-sml0ww?file=/src/Component.js:151-1600
Assuming that the goal is to keep all the selections in the same group field, which must be an array that logs the selected values in provided order, with the custom input value as the last item if specified, perhaps ideally it would be easier to calculate the values in onSubmit before submitting.
But since the preference is not to add logic in onSubmit, maybe an alternative option could be hosting a local state, run the needed calculations when it changes, and call setValue manually to sync the calculated value to the group field.
Forked demo with modification: codesandbox
import "./styles.css";
import { Controller, useFormContext } from "react-hook-form";
import React, { useState, useEffect } from "react";
export const Component = ({ name, ITEMS }) => {
const { control, setValue } = useFormContext();
const [state, setState] = useState({});
useEffect(() => {
const { custom, ...items } = state;
const newItems = Object.entries(items).filter((item) => !!item[1]);
newItems.sort((a, b) => a[0] - b[0]);
const newValues = newItems.map((item) => item[1]);
if (custom) {
setValue(name, [...newValues, custom]);
return;
}
setValue(name, [...newValues]);
}, [name, state, setValue]);
const handleCheck = (val, idx) => {
setState((prev) =>
prev[idx] ? { ...prev, [idx]: null } : { ...prev, [idx]: val }
);
};
const handleCheckCustom = (checked) =>
setState((prev) =>
checked ? { ...prev, custom: "" } : { ...prev, custom: null }
);
const handleInputChange = (e) => {
setState((prev) => ({ ...prev, custom: e.target.value }));
};
return (
<Controller
name={name}
control={control}
render={({ field, formState }) => {
return (
<>
{ITEMS.map((item, index) => {
const isCustomField = index === ITEMS.length - 1;
return (
<React.Fragment key={index}>
<label>
{item.id}
<input
type="checkbox"
name={name}
onChange={(e) =>
isCustomField
? handleCheckCustom(e.target.checked)
: handleCheck(e.target.value, index)
}
value={item.value}
/>
</label>
{typeof state["custom"] === "string" && isCustomField && (
<input onChange={handleInputChange} type="text" />
)}
</React.Fragment>
);
})}
</>
);
}}
/>
);
};
Ok, so after a while I got the solution. I forked your sandbox and did little changes, check it out here: Save Form values in ReactJS using checkboxes
Basically, you should have an internal checkbox state and also don't register the input in the form, because this would add the input value to the end of the array no matter if that value is "".
Here is the code:
import "./styles.css";
import { Controller, useFormContext } from "react-hook-form";
import { useEffect, useState } from "react";
export const Component = ({ name, ITEMS }) => {
const { control, setValue } = useFormContext();
const [state, setState] = useState(false);
const [checkboxes, setCheckboxes] = useState(
ITEMS.filter(
(item, index) => index !== ITEMS.length - 1
).map(({ value }, index) => ({ value, checked: false }))
);
useEffect(() => {
setValue(name, []); //To initialize the array as empty
}, []);
const [inputValue, setInputValue] = useState("");
const handleChangeField = (val) => {
const newCheckboxes = checkboxes.map(({ value, checked }) =>
value == val ? { value, checked: !checked } : { value, checked }
);
setCheckboxes(newCheckboxes);
const response = newCheckboxes
.filter(({ checked }) => checked)
.map(({ value }) => value);
return state && !!inputValue ? [...response, inputValue] : response;
};
const handleChangeInput = (newInputValue) => {
const response = checkboxes
.filter(({ checked }) => checked)
.map(({ value }) => value);
if (state) if (!!newInputValue) return [...response, newInputValue];
return response;
};
return (
<Controller
name={name}
control={control}
render={({ field, formState }) => {
return (
<>
{ITEMS.map((item, index) => {
return (
<>
<label>
{item.id}
<input
type="checkbox"
name={`${name}[${index}]`}
onChange={(e) => {
if (index === ITEMS.length - 1) {
setState(e.target.checked);
return;
}
field.onChange(handleChangeField(e.target.value));
}}
value={item.value}
/>
</label>
{state && index === ITEMS.length - 1 && (
<input
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
field.onChange(handleChangeInput(e.target.value));
}}
type="text"
/>
)}
</>
);
})}
</>
);
}}
/>
);
};

React input value is not changing

My input Tag is here:
<Input
value={isNaN(currentCount) ? '' : currentCount}
onKeyUp={(e) => {
onKeyup(e);
if (e.key === 'Enter') {}
}}
onChange={(e) => {
handleChange(e);
}}
/>
onKeyUp is working, but onChange is not working now.
I found that the value of the input tag does not change when I enter a value in the textbox.
Please let me know if you have had the same problem as me or know how to fix it.
You should create a state and set your input value in it like below:
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}
return (
<Input
value={value}
onKeyUp={(e) => {
onKeyup(e);
if (e.key === 'Enter') {}
}}
onChange={handleChange}
/>
)
const [ currentCount, setCurrentCount] = useState('');
const handleChange = (e) => {
setCurrentCount( e.target.value || '');
}
return (
<input type="text" value={ isNaN(currentCount) ? '' : currentCount } onChange={ (e) => handleChange(e) }/>
)

React js Validation textfield

I am trying to make a validation in reactjs.
For example, I have a field called "name" and every time you input a name, this has to be bigger or equal to 2. It works when the name is smaller than 2, but if it's bigger than 2 the message still appears.
Here is my code:
const [data, setData] = React.useState({
nameValid: true,
prenumeValid: true,
check_textInputChange: false,
name: "",
});
const handleValidUser = (val) => {
if (val.length >= 2) {
setData({
...data,
nameValid: true,
});
} else {
setData({
...data,
nameValid: false,
});
}
};
const textInputChange = (val) => {
if (val.length >= 2) {
setData({
...data,
name: val,
nameValid: true,
check_textInputChange: true,
});
} else {
setData({
...data,
name: val,
nameValid: false,
check_textInputChange: false,
});
}
};
<TextField
type="text"
variant="outlined"
label="Nume"
required
fullWidth
autofocus
validate
style={{ marginBottom: "1em" }}
onChange={(val) => textInputChange(val)}
onBlur={(e) => handleValidUser(e)}
/>
{data.nameValid ? null : (
<text>The name has to be at least 2 letters long</text>
)}
onChange has an event parameter, if you want to get the value of the TextField, you should write it as below.
const textInputChange = e => {
const val = e.target.value;
if (val.length > 2) {
setData(...)
} else {
setData(...)
}
}
return (
<TextField
...
onChange={textInputChange}
onBlur={handleValidUser} // as well as onBlur
/>
)
I've created a codesandbox to demonstrate here.
Call the function as below will create an extra render every time the function is called. You should avoid it.
onChange={(val) => textInputChange(val)}
Can you try changing
onChange={(val) => textInputChange(val)}
to this:
onChange={(e) => textInputChange(e.target.value)}
in function textInputChange you are passing event object instead of input value
you have to change onchange handler from:
onChange={(val) => textInputChange(val)}
to
onChange={(val) => textInputChange(val.target.value)}

update is not capturing and unable to update the input field

please find below code which contains name id and am rendering initially using map
am replacing id value to input type in UI
with the updated input type am trying to update the value onchange
update is not capturing and unable to update the input field
any suggestion?
please refer below snippet
import React, { useState } from "react";
const CstmInput = (props) => {
return (
<input
name={props.name}
type="text"
value={props.value}
onChange={(event) => props.onInputChange(event)}
/>
);
};
export default CstmInput;
import React, { useState } from "react";
import CstmInput from "./CstmInput";
const HierarcyTest = () => {
let rowData = [
{ name: "first", id: 10 },
{ name: "second", id: 20 },
];
const [data, setData] = useState(rowData);
const [name, setName] = useState({ fn: "test" });
const onInputChange = (e) => {
console.log("---event---", e.target.value);
setName({ ...name, fn: e.target.value });
};
let updateValue = () => {
let newData = data.map(
(item, index) =>
(item.id = (
<CstmInput name={item.name} value={item.id} onInputChange={(e) => onInputChange(e)} />
))
);
setData([...data, newData]);
};
return (
<div>
<div>Testing</div>
{data.map((val) => (
<h6>
{" "}
{val.name} {val.id}
</h6>
))}
<button onClick={updateValue}> Click </button>
</div>
);
};
export default HierarcyTest;
A few things why your code isn't working as intended:
1.
let updateValue = () => {
let newData = data.map((item, index) => {
if (item.id === 10) {
return [
(item.id = (
<CstmInput
value={item.id}
onInputChange={(e) => onInputChange(e)}
/>
)),
];
}
});
setData([...data, newData]);
};
In the above function inside the callback of map, you're only returning when a condition satisfies. Are you trying to filter the array instead? If not then return something when the if condition fails.
And why are you returning an array?
return [
(item.id = (
<CstmInput
value={item.id}
onInputChange={(e) => onInputChange(e)}
/>
)),
];
the above code seems logically wrong.
2.
const onInputChange = (e) => {
console.log("---event---", e.target.value);
setName({ ...name, fn: e.target.value });
};
If you want to update state which depends on the previous state then this is how you do it:
setName((prevState) => ({ ...prevState, fn: e.target.value }));
but since you're not actually relying on the properties of the previous state you can just use:
setName({fn: e.target.value });
Note that since your state only has one property and you want to update that single property you can completely overwrite the state, you don't need to spread the previous state.
update
change the updateValue function as the following:
let updateValue = () => {
setData(prevData => {
return prevData.map(el => {
return { ...el, id: <CstmInput value={el.id} onInputChange={(e) => onInputChange(e)} /> };
})
});
};
A stackblitz example I've created that implements what you're trying to do.

setState an array of Objects updating two values in the Object accordingly of the id

I don't know how to target the specific object with the id an change the name and the email value together in one setState.(The members will take new objects with different id)
state = {
groupName:"",
members: [
{name:"", email:"", id:uuid.v4()}
]
}
render(){
return(
{this.state.members.map((member) => {
return (
<div key={member.id}>
<TextField
id={member.id}
placeholder="Enter your name..."
onChange={(event) => this.handleInput(event)} />
<TextField
id={member.id}
placeholder="Enter your email"
onChange={(event) => this.handleInput(event )}
/>
</div>
)
})}
)}
handleInput = (event) => {
const newGroup = {...this.state}
const newElement = {...newGroup.members[event.target.id]}
newElement[name] = event.target.value;
newGroup.members[name] = newElement;
this.setState({
state : newGroup
})
}
You need to pass the field value in order to set the correct element within the members object. Also while updating you can map over the members array and update the respective object and in setState you would pass the members as key.
state = {
groupName:"",
members: [
{name:"", email:"", id:uuid.v4()}
]
}
render(){
return(
{this.state.members.map((member) => {
return (
<div key={member.id}>
<TextField
id={member.id}
placeholder="Enter your name..."
onChange={(event) => this.handleInput(event, "name")} />
<TextField
id={member.id}
placeholder="Enter your email"
onChange={(event) => this.handleInput(event, "email" )}
/>
</div>
)
})}
)}
handleInput = (event, name) => {
event.persist();
this.setState(prevState => ({
members : prevState.members.map((item) => {
if (item.id = event.target.id) {
return {
...item,
[name]: event.target.value
}
} else {
return item;
}
})
}))
}
This is because state should be immutable. You can do following:
this.setState(({members})=> {
const updateMembers = members.map(member => {
if (member.id === event.target.id) {
return {
... member,
[name]: event.target.value
}
}
return member;
})
return {members: updateMembers}
})
You can read more about immutability here

Categories

Resources