State is not updating with correct information - reactjs

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

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}
}
}

How to deal with optional field input in React controlled forms

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,
}));
}
};

Redux/Reducer correct way to spread previous unchanged nested state

In redux or reducer pattern, for nested objects do you need to deeply spread every level of the previous state or is that only necessary on the object that you are updating?
For example if you have some state that looks like this:
{
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
}
};
And then a reducer that updates the address:
case UPDATE_ADDRESS:
return {
...state,
address: {
...state.address,
city: 'Baltimore'
}
};
I am just confused wether this is right or not in terms of immutability. Since the ...state will only return the reference to the old name object and not an entire new object. Would I also need to spread ...state.name even though it is not being changed? I know there are a lot of questions about nested state and reducers and they all say that you need to return a new copy but they all show just ...state for the previous state and I haven't been able to find one that describes this and why you don't need to spread all the old nested state if that's the case. Thanks in advance.
Your code is correct. In short, in redux the return value from reducer will become the next value for the store. Only requirement is that we cannot change the state directly (unless you use something like Immutable.js)
What ...state does is it spread the object out. So your return value will become something like this
{
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
},
address: {
streetName: '123 fake st',
city: "New York",
city: 'Baltimore'
}
}
For object that has duplicate key, Javascript will use the one that is defined near the end of the object. For your instance it will reduce to
{
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: 'Baltimore'
}
}
You can read more about this here in the Spread in object literals section
Your understanding of immutability is correct here. With the object above, you will need to spread values as you have done.
As you develop more in redux, and scale up the size of your applications, the design of your redux store will become more important. Creating several reducers and then stitching these together with combineReducers will help reduce complexity.
Here is an example of how you would do it for your case above. Note this is overkill for your use case. I would only split the reducers up if the state gets much more complex:
const nameReducer = (state = initialState, action) => {
if (action.type === 'UPDATE_FIRST_NAME') {
return {
...state,
first: action.value
}
} else if (action.type === 'UPDATE_LAST_NAME'){
return {
...state,
last: action.value
}
}
return state
}
const addressReducer = (state = initialState, action) => {
if (action.type === 'UPDATE_CITY') {
return {
...state,
city: action.value
}
}
return state
}
export default combineReducers({
name: nameReducer,
counter: addressReducer
})
Once you get to grips with redux checkout Redux Tookit: https://redux-toolkit.js.org/tutorials/basic-tutorial. It helps get rid of some of the boilerplate.
EDIT:
For you example above you will need to spread name also to be fully immutable. Consider these examples:
Not immutable
const person = {
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
}
};
const copy = {
...person,
address: {
...person.address,
streetName: 'myHouse'
}
}
copy.name.first = 'Tom';
console.log({ copy, person });
/* result:
{ copy:
{ name: { first: 'Tom', last: 'Smith' },
address: { streetName: 'myHouse', city: 'New York' } },
person:
{ name: { first: 'Tom', last: 'Smith' },
address: { streetName: '123 fake st', city: 'New York' } } }
*/
Immutable - changing the copy does not affect the person object.
const person = {
name: {
first: 'Bob',
last: 'Smith'
},
address: {
streetName: '123 fake st',
city: "New York"
}
};
const copy = {
name: {
...person.name
},
address: {
...person.address,
streetName: 'myHouse'
}
}
copy.name.first = 'Tom';
console.log({ copy, person });
/* result
{ copy:
{ name: { first: 'Tom', last: 'Smith' },
address: { streetName: 'myHouse', city: 'New York' } },
person:
{ name: { first: 'Bob', last: 'Smith' },
address: { streetName: '123 fake st', city: 'New York' } } }
*/

action structure to get state from redux using reducers

I'm using redux actions to set some contacts list. I want to get those contacts using a redux action. but all i get is the contact set in the action. Can you tell me how to get the actual state contact.
action.ts
export const setCurrentContact = (contact: IContactObject) => ({
type: 'SET_CURRENT_CONTACT',
contact,
})
export const getCurrentContact = () => ({
type: 'GET_CURRENT_CONTACT',
contact: { ID: "", Name: "", Email: "", Mobile: "", Landline: "", Website: "", Address: "" },//when using dispatch i get this contact i.e blank details
})
reducer.ts
const initialContact=[{ ID: "507", Name: "xander", Email: "xander.cage#gmail.com", Mobile: "9999999999", Landline: "4026241799", Website: "www.xandercage.com", Address: "california" }]
export const currentContact = (state = initialContact, action) => {
switch (action.type) {
case 'SET_CURRENT_CONTACT':
{
return [
{
ID: action.contact.ID,
Name: action.contact.Name,
Email: action.contact.Email,
Mobile: action.contact.Mobile,
Landline: action.contact.Landline,
Website: action.contact.Website,
Address: action.contact.Address,
}
]
}
case 'GET_CURRENT_CONTACT':
{
console.log(state[0]);
return state
}
default:
return state
}
}
somefile.ts
interface IDispatchFromProps
{
setCurrentContact: any,
getCurrentContact: any,
}
function mapDispatchToProps(dispatch): IDispatchFromProps
{
return{
setCurrentContact: (contact:IContactObject)=>{dispatch(setCurrentContact(contact))},
getCurrentContact: ()=>{dispatch(getCurrentContact())},//this should give me the initial data
}
}
expected output:
getCurrentContact() gives intial data set in reducer.ts
actual output:
data set in contact key of action.ts
The problem is that when you dispatch 'SET_CURRENT_CONTACT' action type, you crush the existing state.
instead of :
return [
{
ID: action.contact.ID,
...rest of your payload
}
]
do this :
return [
...state,
{
ID: action.contact.ID,
...rest of your payload
}
]
Hope it helps !

Resources