updating specific objects of array in reactjs - reactjs

i want update only specific object from default _data i.e (isconformed value to 1) when user click confirm button that should object isconformed value to 1 and api put call to set the change value in data?
const default_data = {
Id: "",
productname: "",
price:"",
isconformed:0,
}
const[data,setData]=useState(default_data)
const OnChangeHandler = (key, value) => {
//console.log(value) i.e value will be 1 here
setData({ ...data, [key]: value });
};
const Confirmproduct = () => {
const res_data = {
isConformed: data.isConfirmed,
};
//API call
axios.put(`${config.APP_CONFIG}/Product/${Id}`, res_data)
.then((res) => {
setdata(res.data)
})
}
onClick={(e) => {
//when button clicked
e.preventDefault();
OnChangeHandler("isConformed", 1);
Confirmproduct()
}}
}
My expected output
{
Id: "",
productname: "",
price:"",
isconformed:1,
}

The setData function is an asynchronous hook which, when called, does not update immediately the value of data as, for example, using default_data["isconfirmed"]=value.
My recommendation is to simplify and remove the handle function as you don't need it
const default_data = {
Id: "",
productname: "",
price:"",
isconformed:0,
}
const[data,setdata]=useState(default_data)
const Confirmproduct = (key, value) => {
// setData({ ...data, [key]: value }); <- Do you really need this?
const res_data = {
isConformed: value
};
//API call
axios.put(`${config.APP_CONFIG}/Product/${Id}, res_data)
.then((res) => {
setdata(res.data)
})
** button click event**
onClick={(e) => {
e.preventDefault();
Confirmproduct("isConformed", 1);
}}

Related

setting up custom error validation in react

I am trying to create some custom error validation in React
I have a values obj in state and an error obj in state that share the same keys
const [values, setValues] = useState({
name: "",
age: "",
city: ""
});
const [err, setErr] = useState({
name: "",
age: "",
city: ""
});
i have a very simple handle change and an onSubmit which i want to run my custom validator function inside
const handleChange = (e) => {
setValues({
...values,
[e.target.name]: e.target.value
});
};
const handleSubmit = (e) => {
e.preventDefault();
validateForms();
};
in my validateForms function my theory is since both my pieces of state share the same keys I am trying to see if any of those values === '' if yes match is the same key in the err obj and set that respective value to the error and then do other stuff in JSX
const validateForms = () => {
for (const value in values) {
if (values[value] === "") {
setErr({
...err,
value: `${value} is a required field`
});
}
}
};
I definitely feel like I'm not using setErr properly here. Any help would be lovely.
link to sandbox: https://codesandbox.io/s/trusting-bartik-6cbdb?file=/src/App.js:467-680
You have two issues. First, your error object key needs to be [value] rather than the string value. Second, you're going to want to use a callback function in your state setter so that you're not spreading an old version of the error object:
const validateForms = () => {
for (const value in values) {
if (values[value] === "") {
setErr(err => ({
...err,
[value]: `${value} is a required field`
}));
}
}
};
A more intuitive way to set errors might be to accumulate them all and just set the error state once:
const validateForms = () => {
const errors = {};
for (const value in values) {
errors[value] = values[value] === "" ? `${value} is a required field` : "";
}
setErr(errors);
};

Strange bugs with localStorage with useState react

I tried to save values of the input with localStorage and i have a strange bugs, it doens't load data from localStorage.
1- I set the data of multiple input with setItem in useEffect in the InputFile.js
useEffect(() => {
let validateInputs = {
fullNameValidate: enteredFullNameIsValid,
phoneValidate: enteredPhoneIsValid,
};
setValidation(validateInputs);
const allData = { fullNameEntered, phoneEntered };
localStorage.setItem("data", JSON.stringify(allData));
}, [
enteredFullNameIsValid,
enteredPhoneIsValid,
setValidation,
fullNameEntered,
phoneEntered,
]);
2 - i update the value of the handler in the custom hooks (use-input.js) :
const [enteredValue, setEnteredValue] = useState({
enterValidate: "",
});
const valueChangeHandler = (event) => {
setEnteredValue({
...enteredValue,
enterValidate: event.target.value,
});
};
3- I tried to take the values saved with (in custom hooks, use-input.js):
useEffect(() => {
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue((prevState) => {
return { ...prevState, fullNameEntered, phoneEntered };
});
}, []);
But it doesn't works!
UPDATE:
here there are the 2 complete files:
1- custom hooks for the inputs
const useInput = (validateValue) => {
const [enteredValue, setEnteredValue] = useState({
enterValidate: "",
});
const [isTouched, setIsTouched] = useState(false);
const [clickClasses, setClickClasses] = useState(false);
const valueIsValid = validateValue(enteredValue.enterValidate);
const hasError = !valueIsValid && isTouched;
const valueChangeHandler = (event) => {
setEnteredValue({
...enteredValue,
enterValidate: event.target.value,
});
};
const inputBlurHandler = () => {
setIsTouched(true);
setClickClasses(false);
};
const inputClickHandler = () => {
setClickClasses(!clickClasses);
};
const reset = () => {
setEnteredValue("");
setIsTouched(false);
};
////TAKE STORED DATA////// BUT IT DOESN'T WORK
useEffect(() => {
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue((prevState) => {
return { ...prevState, fullNameEntered, phoneEntered };
});
}, []);
console.log(enteredValue);
return {
value: enteredValue.enterValidate,
isValid: valueIsValid,
hasError,
valueChangeHandler,
inputBlurHandler,
inputClickHandler,
click: clickClasses,
reset,
};
};
export default useInput;
2- Input files
import React, { useEffect } from "react";
import useInput from "../../../../../hooks/use-input";
import validateInput from "../../../../../utils/validateInput";
import {
WrapperNamePhone,
LabelNamePhone,
SpanInputDescription,
InputStyle,
} from "../ContactFormInput.style";
export default function FullNamePhoneInput({ setValidation }) {
//FullName Input
const {
value: fullNameEntered,
isValid: enteredFullNameIsValid,
hasError: fullNameHasError,
valueChangeHandler: fullNameChangeHandler,
inputBlurHandler: fullNameBlurHandler,
inputClickHandler: fullNameClickHandler,
click: fullNameClickClasses,
} = useInput((value) => {
const inputValidFullName = {
value: value,
maxLength: 20,
whiteSpace: true,
allowNumber: false,
allowStrings: true,
};
return validateInput(inputValidFullName);
});
//Phone numbers input
const {
value: phoneEntered,
isValid: enteredPhoneIsValid,
hasError: phoneHasError,
valueChangeHandler: phoneChangeHandler,
inputBlurHandler: phoneBlurHandler,
} = useInput((value) => {
const inputValidPhone = {
value: value,
maxLength: 15,
whiteSpace: true,
allowNumber: true,
allowStrings: false,
};
return validateInput(inputValidPhone);
});
useEffect(() => {
let validateInputs = {
fullNameValidate: enteredFullNameIsValid,
phoneValidate: enteredPhoneIsValid,
};
setValidation(validateInputs);
//STORE DATA////
const allData = { fullNameEntered, phoneEntered };
localStorage.setItem("data", JSON.stringify(allData));
}, [
enteredFullNameIsValid,
enteredPhoneIsValid,
setValidation,
fullNameEntered,
phoneEntered,
]);
//FUll NAME
const borderColorFullName = fullNameHasError ? `rgb(245, 2, 2)` : `#d5d9dc`;
const clickedColor = fullNameClickClasses ? "#2696e8" : "#a4aeb4";
//PHONE NUMBERS
const borderColorPhone = phoneHasError ? `rgb(245, 2, 2)` : `#d5d9dc`;
return (
<WrapperNamePhone>
<LabelNamePhone htmlFor="full-name">
<SpanInputDescription clickedColor={clickedColor}>
Full name
</SpanInputDescription>
<InputStyle
type="text"
name="full-name"
id="full-name"
borderColor={borderColorFullName}
value={fullNameEntered}
onChange={fullNameChangeHandler}
onBlur={fullNameBlurHandler}
onClick={fullNameClickHandler}
/>
{fullNameHasError && <p> - Enter a valid Full Name</p>}
</LabelNamePhone>
<LabelNamePhone htmlFor="phoneNumber">
<InputStyle
placeholder="Enter a valid phone number"
type="text"
name="phoneNumber"
borderColor={borderColorPhone}
value={phoneEntered}
onChange={phoneChangeHandler}
onBlur={phoneBlurHandler}
/>
{phoneHasError && <p> - Enter a valid Phone Number</p>}
</LabelNamePhone>
</WrapperNamePhone>
);
}
///////////////////////////
//////////////////////////
Final Update, i don't like this solution but it works!
I've done like this:
-1 I deleted the setKeys from the Input files.
-2 I update the setKeys dinamically in the use-input hooks:
-3 then with useState i update the getItem!
const [enteredValue, setEnteredValue] = useState(() => {
return {
validateInput: "",
fullName: JSON.parse(localStorage.getItem("fullName")) || "",
phoneNumber: JSON.parse(localStorage.getItem("phoneNumber")) || "",
email: JSON.parse(localStorage.getItem("email")) || "",
country: JSON.parse(localStorage.getItem("country")) || "",
From: JSON.parse(localStorage.getItem("From")) || "",
To: JSON.parse(localStorage.getItem("To")) || "",
};
});
const valueChangeHandler = (event) => {
setEnteredValue({
[event.target.name]: event.target.value,
validateInput: event.target.value,
});
localStorage.setItem(event.target.name, JSON.stringify(event.target.value));
};
*/ Other code for validation, useless in this example */
return {
value: enteredValue.validateInput,
valueName: enteredValue.fullName,
valuePhone: enteredValue.phoneNumber,
valueEmail: enteredValue.email,
valueCountry: enteredValue.country,
valueFrom: enteredValue.From,
valueTo: enteredValue.To,
isValid: valueIsValid,
hasError,
valueChangeHandler,
inputBlurHandler,
inputClickHandler,
click: clickClasses,
reset,
};
I m not sure i have completely understood your problem, however instead of doing all that in useState you could use an effect and set its value based on any dependency or just on component mount like so:
const [enteredValue, setEnteredValue] = useState({});
useEffect(()=>{
const saved = localStorage.getItem("data");
const { fullNameEntered, phoneEntered } = JSON.parse(saved);
setEnteredValue({
...enteredValue,
fullname:fullNameEntered,
phone:phoneEntered
})
},[])
you can change to add some conditions to ensure its not null before being set etc. but this approach should work in general instead of using a callback in useState.
You can then use the values in the component via the state i.e enteredValue?.fullname etc. (?. is optional chaining to prevent undefined errors)

TypeError: inputs.lineItems is undefined React

const [inputs, setInputs] = useState({
taxRate: 0.00,
lineItems: [
{
id: 'initial',
name: '',
description: '',
quantity: 0,
price: 0.00,
},
]
});
function handleInvoiceChange(e) {
//setInputs(inputs => ({...inputs,[e.target.name]: e.target.value}));
setInputs({[e.target.name]: e.target.value});
}
const calcLineItemsTotal = (event) => {
return inputs.lineItems.reduce((prev, cur) => (prev + (cur.quantity * cur.price)), 0)
}
const calcTaxTotal = () => {
return calcLineItemsTotal() + (inputs.taxRate / 100)
}
and this is how i handle the change
const handleLineItemChange = (elementIndex) => (event) => {
let lineItems = inputs.lineItems.map((item, i) => {
if (elementIndex !== i) return item
return {...item, [event.target.name]: event.target.value}
})
setInputs(inputs => ({...inputs,[lineItems]:lineItems}));
//setInputs({lineItems})
}
const handleAddLineItem = (event) => {
setInputs({
lineItems: inputs.lineItems.concat(
[{ id: uuidv4(), name: '', description: '', quantity: 0, price: 0.00 }]
)
})
}
const handleRemoveLineItem = (elementIndex) => (event) => {
setInputs({
lineItems: inputs.lineItems.filter((item, i) => {
return elementIndex !== i
})
})
}
this is a react application of an invoice generator the problem occures when i add the taxrate then i get that error
Updated values to states with hooks are not merged but replaced.
Also if you are using a version of v16 or lower of react know that Synthetic event is pooled by react, i.e event object is cleared before state callback runs.
Check here for more information.
The SyntheticEvent objects are pooled. This means that the
SyntheticEvent object will be reused and all properties will be
nullified after the event handler has been called.
The correct way to update your state is as below where you use function way to update state and copy the event values you need to separate variables outside of the setInputs function call
const name = e.target.name;
const value = e.target.value;
setInputs(inputs => ({...inputs,[name]: value}));
The rest of your function updates will be as below
const handleAddLineItem = (event) => {
setInputs(input => ({
...input,
lineItems: inputs.lineItems.concat(
[{ id: uuidv4(), name: '', description: '', quantity: 0, price: 0.00 }]
)
}));
}
const handleRemoveLineItem = (elementIndex) => (event) => {
setInputs(input =>({
...input,
lineItems: inputs.lineItems.filter((item, i) => {
return elementIndex !== i
})
}));
}

Use of prevState to change value in array of objects does not return expected value

I am using the following method to try to change the value of task.isComplete to !task.isComplete onClick.
handleComplete = (event) => {
event.preventDefault();
this.setState(
(prevState) => ({
listOfTasks: prevState.listOfTasks.map((task) => {
if (task.id === event.target.id) {
task.isComplete = !task.isComplete;
console.log(task);
}
return task;
}),
}),
() => console.log(this.state.listOfTasks)
);
};
when clicking the button the 2 logs are:
{nameOfTask: "aaaa", isComplete: true, id: "1610746018062"}
TodoListTest.js:54
[{…}]
0: {nameOfTask: "aaaa", isComplete: false, id: "1610746018062"}
length: 1
__proto__: Array(0)
React seems to only consider the second log state so I don't get the expected change, incomplete turning to true at the end of the operation.
you need to make a shallow copy of the task object to update its isComplete property.
handleComplete = (event) => {
event.preventDefault();
this.setState(
(prevState) => ({
listOfTasks: prevState.listOfTasks.map((task) => {
if (task.id === event.target.id) {
return {
...task,
isComplete: !task.isComplete
};
}
return task;
})
}),
() => console.log(this.state.listOfTasks)
);
};

How can I see state within a function? using hooks

I'm trying to update the uploadFiles state inside my updateFile function, when reloading the file, I'm rewriting this component in hooks, but inside the function the state is given as empty.
const [uploadedFiles, setUploadedFiles] = useState({
slides: [],
material: [],
});
const updateFile = useCallback(
(id, data) => {
const value = uploadedFiles.slides.map(uploadedFile => {
return id === uploadedFile.id
? { ...uploadedFile, ...data }
: uploadedFile;
});
console.log('value', value);
console.log('uploadedFilesOnFunction', uploadedFiles);
},
[uploadedFiles]
);
function processUpload(upFile, type) {
const data = new FormData();
data.append('file', upFile.file, upFile.name);
api
.post('dropbox', data, {
onUploadProgress: e => {
const progress = parseInt(Math.round((e.loaded * 100) / e.total), 10);
updateFile(upFile.id, {
progress,
});
},
})
.then(response => {
updateFile(upFile.id, {
uploaded: true,
id: response.data.id,
url: response.data.url,
type,
});
})
.catch(response => {
updateFile(upFile.id, {
error: true,
});
});
}
function handleUpload(files, type) {
const uploaded = files.map(file => ({
file,
id: uniqueId(),
name: file.name,
readableSize: filesize(file.size),
preview: URL.createObjectURL(file),
progress: 0,
uploaded: false,
error: false,
url: null,
type,
}));
setUploadedFiles({
slides: uploadedFiles.slides.concat(uploaded),
});
uploaded.forEach(e => processUpload(e, type));
}
console.log('slides', uploadedFiles);
I expected the state values to be viewed by the function. For me to manipulate and set the state.
There might be other issues, but one thing I've noticed is:
const [uploadedFiles, setUploadedFiles] = useState({
slides: [],
material: [],
});
// A setState CALL FROM THE useState HOOK REPLACES THE STATE WITH THE NEW VALUE
setUploadedFiles({
slides: uploadedFiles.slides.concat(uploaded),
});
From: https://reactjs.org/docs/hooks-state.html
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.
The setState from the useState hook doesn't merge the state. Because it can hold any type of value, not only objects, like we used to do with classes.
From your code you can see that you're erasing some property from state when you're updating like that.
Instead, you should use the functional form of the setState and access the current state prevState, like:
setUploadedFiles((prevState) => {
return({
...prevState,
slides: uploadedFiles.slides.concat(uploaded)
});
});
The updated updateFiles function:
const updateFile = (id, data) => {
setUploadedFiles(prevState => {
const newSlide = prevState.slides.map(slide => {
return id === slide.id ? { ...slide, ...data } : slide;
});
return {
...prevState,
slides: newSlide,
};
});
};

Resources