push an element to an array with useState hooks - reactjs

Im trying to push new tag in useState how possible i can do that
const [mydata, setData] = useState({
user_gender: "",
user_relationship: "",
user_birth_day: "",
user_birth_month: "",
user_gender_interest: "",
user_birth_year: "",
tags: []
});
const handleAddChip = chip => {
setData({
...mydata,
tags: chip
});
};

Use the updater version of the setter provided by useState and concat to return a new array instead of push
const handleAddChip = chip => {
setData(previousData =>({
...previousData,
tags: previousData.tags.concat(chip)
}));
};
You can also do it without the updater version, but usually isn't a good idea.
setData({...myData, tags: myData.tags.concat(chip)})

Related

React Functional component - UseState with array index

i am new in React. "valueForm" is my state and i'm struggling to assign values to course and institute, those values are dynamic,
i need to get fields as:
course: ['MSc','BSc'],
institute: ['PG','Digree']
const [valueForm, setvalueForm] = useState({
first_name: "",
last_name: "",
course: [],
institute: [],
});
const handlechanage = (event, index) => {
// console.log('index',index);
setvalueForm({ ...valueForm, [event.target.name]: event.target.value });
};
index is from dynamic fields.
I need to perform this inside handlechanage function for all fields of valueForm state.
Try using functional updates as the value is dependent on previous state
const handlechanage = (event, index) => {
// console.log('index',index);
setvalueForm((current) => ({ ...curr, [event.target.name]: event.target.value }));
};

Redux/Toolkits, useSelector Doesn't Work, Why?

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.

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

onChange effect after second entry [duplicate]

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

setState to nested object delete value

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 (
...
);
};

Resources