How to get values of map object and assigned to useform defaultValue.
const def = jobdetail.defect_details;
const sample = def?.map((item) => {
return {
defects: item.defects,
recommendation: item.recommendation,
photo: []
};
});
const defects_json = JSON.stringify(sample);
const output = defects_json?.replace(/"(\w+)":/g, '$1:');
// result of const output when console.log(output)
[{defects:"defect 2",recommendation:"recomendatin 2",photo:[]},{defects:"defect1 ",recommendation:"recommendation 1",photo:[]}]
this is my useform
const {
register,
formState: { errors },
handleSubmit,
reset,
control,
getValues,
setValue,
} = useForm({
shouldFocusError: false,
defaultValues: {
defectslist: output, // its empty
partslist: [
{ sorCode: "", item: "", quantity: "", rates: "", subtotal: "" },
],
},
});
Hope someone can help me to why const output is empty in useform defaultValue
the keys of your defaultValues need to correspond to the name prop of your input components. So your defaultValues object essentially needs to look something like this:
{
defects0: "defect 2",
recommendation0: "recomendatin 2",
photo0: []
defects1: "defect 2",
recommendation1: "recomendatin 2",
photo1:[],
sorCode: "",
item: "",
quantity: "",
rates: "",
subtotal: ""
}
And you html must look something like:
<input name="defects0" ref={register}>
<input name="recommendation0" ref={register}>
To achieve the first one you can do something like this:
const defaultValuesObj = { sorCode: "", item: "", quantity: "", rates: "", subtotal: "" }
def?.forEach((item, i) => {
defaultValuesObj[`defects${i}`] = item.defects
defaultValuesObj[`recommendation${i}`] = item.recommendation
defaultValuesObj[`photo${i}`] = item.photo
});
and you can do defaultValues: defaultValuesObj
I have already fixed this issue. I need to pass object to reset() from useEffect to re-render the values:
const {
register,
formState: { errors },
handleSubmit,
control,
getValues,
setValue,
reset,
initialState
} = useForm();
useEffect(() => {
reset(jobdetail);
}, [jobdetail])
Related
I want to save my data in localstorage to evade the loss of it when reloading the page but i also need it in my gloable state to show a preview of it once it's added and never be lost when reloading the page,This is my slice format:
import { createSlice } from "#reduxjs/toolkit";
export const resumeSlicer = createSlice({
name: "resume",
initialState: {
Education: [
{
key: NaN,
Title: "",
Date: "",
Establishment: "",
Place: "",
},
],
},
reducers: {
SaveEducation: (state, action) => {
let Education = JSON.parse(localStorage.getItem("Education"));
if (!Education) {
Education.push(action.payload);
localStorage.setItem("Education", JSON.stringify(Education));
state.Education = Education;
} else {
Education.push(action.payload);
let i = 0;
Education.map((e) => {
e.key = i;
i++;
return e.key;
});
localStorage.setItem("Education", JSON.stringify(Education));
state.Education = Education;
}
},
getEducation: (state, action) => {
const items = JSON.parse(localStorage.getItem("Education"));
const empty_array = [
{
key: NaN,
Title: "",
Date: "",
Establishment: "",
Place: "",
},
];
state.Education.splice(0, state.Education.length);
state.Education = items;
},
},
});
And this is how i fetched:
const EdList = useSelector((state) => state.Education);
When i console.log it the result is "undefined"
Image Preview
https://i.stack.imgur.com/hD8bx.png
I'm hazarding a guess that the issue is a missing reference into the state. The chunk of state will typically nest under the name you give the slice, "resume" in this case. This occurs when you combine the slice reducers when creating the state object for the store.
Try:
const EdList = useSelector((state) => state.resume.Education);
If it turns out this isn't the case then we'll need to see how you create/configure the store and how you combine your reducers.
I have onChange handler but when I type it removes the lng value from the whole object
So I have an object stored in State.
const [inputFields, setInputFields] = useState({
style: "",
coordinates: { lat: 51.501476, lng: -0.140634 },
assetId: "",
});
The onChange handler
const handleLat = (event) => {
setInputFields({ ...inputFields, coordinates: {[event.target.name]: parseInt(event.target.value)} })
};
Which results in
{
"style": "",
"coordinates": {
"lat": 23434434287
},
"assetId": ""
}
I would like to retain the "lng" value and not override the whole coordinates object.
Following above thread, You can do this:
setInputFields(inputFields => ({ ...inputFields, coordinates: {...inputFields.coordinates,[event.target.name]: parseInt(event.target.value)} }))
try following :
const handleLat = (event) => {
setInputFields(
{ ...inputFields,
coordinates: {[event.target.name]: parseInt(event.target.value), ...inputFields.coordinates}
})
};
This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 1 year ago.
I'm a bit new with ReactJS and I have a problem with my onChange function, it starts displaying after the second entry and it is one letter late every time. And I clearly don't understand why
Here is my code:
const [values, setValues] = useState({
latinName: "",
wingLength: "",
weight: "",
adiposity: "",
age: "",
howCaptured: "",
whenCaptured: "",
whereCaptured: "",
ringNumber: "",
takeover: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
setValues({
...values,
[name]: value,
});
console.log(values);
}
<input type="text" name="latinName" id="latinName" onChange={handleChange} value={values.latinName} />
Here my input
Here my result
useState hook's setState works async. As a result, during state update, you are still getting the previous value in the mean time.
You can use useEffect hook in your case.
useEffect(() => console.log(values), [values]);
Full code:
import { useEffect, useState } from "react";
export default function App() {
const [values, setValues] = useState({
latinName: "",
wingLength: "",
weight: "",
adiposity: "",
age: "",
howCaptured: "",
whenCaptured: "",
whereCaptured: "",
ringNumber: "",
takeover: ""
});
useEffect(() => console.log(values), [values]);
const handleChange = (e) => {
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
};
return (
<input
type="text"
name="latinName"
id="latinName"
onChange={handleChange}
value={values.latinName}
/>
);
}
CodeSandBox Demo
I have the following state:
const [state, setState] = React.useState({
title: "",
exchangeTypes: [],
errors: {
title: "",
exchangeTypes: "",
}
})
I am using a form validation in order to populate the state.errors object if a condition is not respected.
function formValidation(e){
const { name, value } = e.target;
let errors = state.errors;
switch (true) {
case (name==='title' && value.length < 4):
setState(prevState => ({
errors: { // object that we want to update
...prevState.errors, // keep all other key-value pairs
[name]: 'Title cannot be empty' // update the value of specific key
}
}))
break;
default:
break;
}
}
When I do so, the object DOES update BUT it deletes the value that I have not updated.
Before I call formValidation
My console.log(state) is:
{
"title": "",
"exchangeTypes: [],
"errors": {
title: "",
exchangeTypes: "",
}
}
After I call formValidation
My console.log(state) is:
{
"errors": {
title: "Title cannot be empty",
exchangeTypes: ""
}
}
SO my other state values have disappeared. There is only the errors object.
I followed this guide: How to update nested state properties in React
What I want:
{
"title": "",
"exchangeTypes: [],
"errors": {
title: "Title cannot be empty",
exchangeTypes: "",
}
}
What I get:
{
"errors": {
title: "Title cannot be empty",
exchangeTypes: "",
}
}
unlike the setState in class component, setState via useState doesn't automatically merge when you use an object as the value. You have to do it manually
setState((prevState) => ({
...prevState, // <- here
errors: {
// object that we want to update
...prevState.errors, // keep all other key-value pairs
[name]: 'Title cannot be empty', // update the value of specific key
},
}));
Though you can certainly use the useState hook the way you're doing, the more common convention is to track the individual parts of your component's state separately. This is because useState replaces state rather than merging it, as you've discovered.
From the React docs:
You don’t have to use many state variables. State variables can hold objects and arrays just fine, so you can still group related data together. However, unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
So in practice, your code might look like the following:
const MyComponent = () => {
const [title, setTitle] = useState('');
const [exchangeTypes, setExchangeTypes] = useState([]);
const [errors, setErrors] = useState({
title: "",
exchangeTypes: "",
});
function formValidation(e) {
const { name, value } = e.target;
switch (true) {
case (name === 'title' && value.length < 4):
setErrors({
...errors,
[name]: 'Title cannot be empty'
});
break;
default:
break;
}
}
return (
...
);
};
Hello guys I'm trying to update the state of a nested object in react, I'm currently doing this:
handleChange({target: {id, value}}, type) {
this.setState(
state => ({
dwelling: (Object.assign(state.dwelling, {[id]: {[type]: value}}))
})
);
}
it comes from a formgroup:
<FormGroup controlId="spaces">
<ControlLabel>Dormitorios</ControlLabel>
<FormControl
componentClass="select"
value={dwelling.spaces.dorms}
placeholder="Seleccione"
onChange={e => this.handleChange(e, 'dorms')}
>
The problem is when I update the state of the sub object dwelling.spaces.dorms is created but when I try to place another property it replaces the old one instead of getting added:
Before Dwelling:
{
address: "",
currency: "",
price: 0,
publicationType: "",
spaces: {
closets: "",
dorms: "",
pools: ""
},
subtype: "",
type: ""
}
After onChange for dwelling.spaces.dorms
{
address: "",
currency: "",
price: 0,
publicationType: "",
spaces: {
dorms: "3",
},
subtype: "",
type: ""
}
After onChange for dwelling.spaces.closets
{
address: "",
currency: "",
price: 0,
publicationType: "",
spaces: {
closets: "3",
},
subtype: "",
type: ""
}
This example uses ES6 spread operator to keep your old properties which is the equivalent of Object.assign.
So what was happening is you're not keeping your nested value.
this.setState({
dwelling: {
...this.state.dwelling,
[id]: {
...this.state.dwelling[id],
[type]: value
}
}
});
In your example you overwrote your value with a new object. Notice the bolded text below.
dwelling: (Object.assign(state.dwelling, {[id]: {[type]: value}}))
In the bolded text it specifically set a new object into state.dwelling without keeping the old values. So what we did is that we used the ES6 spread operator to help merge your old values with the new value
{
...this.state.dwelling[id],
[type]: value
}
I keep my form state in a complex object and to simplify my code I use this helper function in my "handleChange" function referenced by TextField, Select, etc..
export function updateObject(obj, keys, value) {
let key = keys.shift();
if (keys.length > 0) {
let tmp = updateObject(obj[key], keys, value);
return {...obj, [key]: tmp};
} else {
return {...obj, [key]: value};
}
}
React-redux/Material-UI example
let [formState, changeFormState] = useState({});
function handleChange(event) {
changeFormState(updateObject(formState, event.target.name.split('.'),
event.target.value));
}
<TextField className={classes.textfield} name='foo.bar' value=
formstate.foo.bar || ''} onChange={handleChange} />