How to deal with optional field input in React controlled forms - reactjs

Let's say I have a form with 3 fields: firstName, middleName, lastName. The fields firstName and lastName are mandatory, but middleName is optional. So, when someone completes the form without writing his middle name, the object that is produced by the form is something like
{
firstName: "John",
middleName: "",
lastName: "Doe",
}
but I would like to get something like
{
firstName: "John",
middleName: undefined,
lastName: "Doe",
}
Of course I could manually clean the resulting object, and convert "" to undefined, but I wonder if there is a better way.

You can write custom logic to handle this:
const initialValues = {
firstName: "",
lastName: "",
middleName: ""
}
// Set use state
const [values, setValues] = useState(initialValues);
const handleMiddleNameInputChange = (event) => {
event.persist();
if(middleName === ""){
setValue((values)=>({
...values,
middleName: "undefined"
}));
}else{
setValues((values) => ({
...values,
middleName: event.target.value,
}));
}
};

Related

Select option value returns '[object Object]'

I would like to ask for your help. I have mapped a list of data patientOptions to select field, each option has a value of
value: {
id: patient.id,
firstName: patient.firstName,
middleName: patient.middleName,
lastName: patient.lastName,
},
I need to store this information when I submit the form. If I console.log(patientOptions) this will appear
What I am trying to achieve is to get all values
value: {
id: 13412341234,
firstName: John,
middleName: Made,
lastName: Doe,
},
But if I will try to submit the form and log it to the console this will show up.
I have also tried just getting the value of 1 object it works but trying to get the value of multiple objects still returns the same error
Here is the rest of the code I hope anyone can help me.
const patientOptions = patient.map((patient) => ({
key: `${patient.firstName} ${patient.middleName} ${patient.lastName},
value: {
id: patient.id,
firstName: patient.firstName,
middleName: patient.middleName,
lastName: patient.lastName,
},
}));
onSubmit = (values) =>
console.log("form data", {
patient: values.patient,
});
<Formik
initialValues={{
patient: "",
}}
onSubmit={onSubmit}
>
<Form>
<FormikControl
control="select"
name="patient"
label="Patient"
options={patientOptions}
/>
</Form>
</Formik>;

I cant find a way to dynamically enter value in the object field which includes a array

this is the object . How can i input value through form tag in listItem key dynamically in react
{
name: "",
dueDate: "",
grossAmount: "",
billNo: "",
billDate: "" ,
listItem:[{ productName: "", quantity: null, price: null, amount: null, gstRate: null}],
gstAmount: null,
netAmount: null,
notes: "",
status: " "
}
Assuming object is stored in a state using useState hook
const [obj,setObj] = useState({
name: "",
dueDate: "",
grossAmount: "",
billNo: "",
billDate: "" ,
listItem:[{ productName: "", quantity: null, price: null, amount: null, gstRate: null}],
gstAmount: null,
netAmount: null,
notes: "",
status: " "
})
You can do..
state for input:
const [productName,setProductName] = useState("");
And for input you can do something like this. Note: I just did this for changing productName, but you can do this for any of the other keys of the obj.
<form>
<input type="text" onChange={(e)=>setProductName(e.target.value)} />
<button onClick={()=>setObj({...obj,lastItem:[...lastItem[0],productName]})}></button>
</form>
First add name attribute to your input element. Then create a onChangeHandler function. This function can handle all changes for every inputs you have.
Your inputs (the name attribute should be the same as your object key):
<input name='dueDate' onChange={onChangeHandler} type="text" value={myObject.dueDate} />
onChangeHandler function:
const onChangeHandler = (event) => {
myObject = {
return {...myObject, [event.targer.name]: event.targer.value}
}
}

What does the meaning of this line of codes ` skills: { ...this.state.skills, [name]: checked }`?

class App extends React.Component {
state = {
firstName: "",
lastName: "",
email: "",
country: "",
tel: "",
dateOfBirth: "",
favoriteColor: "",
weight: "",
gender: "",
file: "",
bio: "",
skills: {
html: false,
css: false,
javascript: false,
},
};
handleChange = (e) => {
const { name, value, type, checked } = e.target;
console.log(e)
if (type === "checkbox") {
this.setState({
skills: { ...this.state.skills, [name]: checked },
});
} else if (type === "file") {
console.log(type, "check here");
this.setState({ [name]: e.target.files[0] });
} else {
this.setState({ [name]: value });
}
console.log(this.state.skills)
};
what does this line do?
skills: { ...this.state.skills, [name]: checked }
The ...this.state.skills may be using the spread operator to copy the object but I do not know the meaning of [name]: checked, the purpose of it is to change the value of key in the skills obj to true but I don know how it can be.
This is a very good question, lots of stuff going on in the following line. So good catch to learn new stuff.
skills: { ...this.state.skills, [name]: checked }
named property
Normally we do obj = { "abc": 3 }, but what if the "abc" is stored in a variable name? That's why this [name] comes to play. Otherwise we have to do obj[name] = 3 which takes additional effort. Also notice the difference between it with obj["name"] = 3. These two versions are very different.
spread operator
If we do a = b for an object, it doesn't make a new memory for a.
To create a new a, we do a = { ...b }. It takes out every first level property out of b and borrow them into a new memory space for a. Essentially a spread operator implies a new variable.
However only a is a new variable, none of the things you borrowed from b is re-created unless they are primitive variables. So we can not call a spread operator a deep copy. Maybe we call it a shallow copy, i don't know the right name ;)

Redux reducer - a single state object or multiple states?

Consider these two patterns for reducer's state:
A single state object:
// personReducer.js
const initialState = {
personInfo: {
firstName: "",
lastName: "",
foo: "",
bar: "",
baz: "",
foo1: "",
bar1: "",
bar2: "",
foo2: "",
foo3: "",
}
// some not-to-mention states
};
Multiple states (without the person level):
// personReducer.js
const initialState = {
firstName: "", // no person level
lastName: "",
foo: "",
bar: "",
baz: "",
foo1: "",
bar1: "",
bar2: "",
foo2: "",
foo3: ""
};
Which one is better? What confuses me is:
// Person.jsx - pattern 1
const mapStateToProps = ({ personInfo }) => {
personInfo
}
// Person.jsx - pattern 2
const mapStateToProps = ({ firstName, lastName }) => {
firstName,
lastName
}
// Human.jsx - pattern 1
const mapStateToProps = ({ personInfo }) => {
personInfo
}
// Human.jsx - pattern 2
const mapStateToProps = ({ foo, bar }) => {
foo,
bar
}
Let's say we have 2 components Person and Human in our app, both of them will connect to personReducer to retrieve personal information.
For pattern 1:
In Person component, I dispatch an action to update firstName inside personInfo, which later will force Human to re-render as well, does it? Something like this in our reducer:
case UPDATE_PERSON_INFO: {
return {
...state,
personInfo: {
...state.personInfo,
firstName: payload.firstName
}
}
}
For pattern 2:
In Person component, I dispatch an action to update firstName, which later will not force Human to re-render, because Human is not mapping firstName to its props, but foo, bar. Am I right? Something
like:
case UPDATE_PERSON_INFO: {
return {
...state,
firstName: payload.firstName
}
}
It will re-render in both cases because in both patterns you update the reference to the new immutable state object.
If you want to prevent unnecessary renderings of components you have to use memoized selectors in mapStateToProps. Here is the documentation link and GitHub
These, selectors should be specific for your components.

State is not updating with correct information

Hello I am trying to update the vale of my state in React but I don't know why it always goes to the option where selectedUser is not defined. I tried putting everything inside an if-else but I got the same results, the strange thing is that when I wrote something in the console it went inside my conditional but it took the empty values instead.
const [inputFieldState, setInputFieldState] = useState(InputFields(selectedUser));
function InputFields(selectedUser: any) {
return selectedUser
? selectedUser
: {
email: "",
firstName: "",
lastName: "",
customerId: "",
id: "",
role: ""
};
}
selectedUser is a fake value for now
return {
firstName: planes[Math.floor(Math.random() * planes.length)],
lastName: planes[Math.floor(Math.random() * planes.length)],
customerId: planes[Math.floor(Math.random() * planes.length)],
id: planes[Math.floor(Math.random() * planes.length)],
role: names[Math.floor(Math.random() * names.length)],
email: Math.ceil(Math.random() * 5)
};
when I do a console log of the selected user I get
{firstName: "x-wing", lastName: "Winnebago", customerId: "y-wing", id: "TIE-fighter", role: "Anakin", …}
customerId: "y-wing"
email: 2
firstName: "x-wing"
id: "TIE-fighter"
lastName: "Winnebago"
role: "Anakin"
From the comments, selectedUser is a function
const [inputFieldState, setInputFieldState] = useState(InputFields(selectedUser()));
function InputFields(user: any) { // just changed the name to avoid potential confusion
return user
? user
: {
email: "",
firstName: "",
lastName: "",
customerId: "",
id: "",
role: ""
};
}
I think you need to change useState(InputFields(selectedUser)) to
useState(() => InputFields(selectedUser))
so that it will not use the Initial state in every render

Resources