I've issues with the checkbox. I want to get the value and name field data in array format to do further processes.
Here's Checkbox:
<input type="checkbox" id="Honda" name="A1" value="Honda" onClick={CheckHandler} />
<label htmlFor="Honda" >Honda</label>
Now, I want to get the name and value field data in JSON or in an array
Like this:
{ name:"A1", value:"Honda" } //I want this.
So, I've coded like this:
import React, { Fragment, useState, useEffect } from "react";
export default function App() {
const [cars, setCars] = useState([]);
const CheckHandler = (e) => {
const value = e.target.value;
const name = e.target.name;
// setCars((prev) =>
// cars.includes(value)
// ? prev.filter((cur) => cur !== value)
// : [...prev, {[e.target.value]:`${e.target.name}`}]
// );
};
useEffect(() => {
console.log(cars);
}, [cars]);
return (
<Fragment>
<input type="checkbox" id="Honda" name="A1" value="Honda" onClick={CheckHandler}/>
<label htmlFor="Honda">Honda</label>
<br/>
<input type="checkbox" id="Mustang" name="A8" value="Mustang" onClick={CheckHandler}/>
<label htmlFor="Mustang">Mustang</label>
<br />
<input type="checkbox" id="Jeep" name="A12" value="Jeep" onClick={CheckHandler}/>
<label htmlFor="Jeep">Jeep</label>
<br />
</Fragment>
);
}
MY PROBLEM: whenever I Tick on checkbox It works fine, But when I unchecked its not returning sets from remaining items. IT's returning from all items. why ??
Any one Knows Answer ??
Sandbox https://codesandbox.io/s/late-water-piszn
Hi I fiddled a bit of your code and
const checkHandler = (e) => {
const value = e.target.value;
const name = e.target.name;
setCars((prev) =>
cars.find(ch => ch[value] === name)
? prev.filter((cur) => cur[value] !== name)
: [...prev, { [e.target.value]: `${e.target.name}` }]
);
};
update your method like this and it's working.
Here is the updated function I made for you answer
const CheckHandler = (e) => {
console.log(cars);
const value = e.target.value;
const name = e.target.name;
var found = false;
for (var i = 0; i < cars.length; i++) {
if (cars[i][value] === name) {
found = true;
break;
}
}
setCars((prev) =>
found
? prev.filter((cur) => cur[value] !== name)
: [...prev, { [value]: name }]
);
};
Here is the sandbox
Related
So Im having problems with a form in which takes in text inputs to be set on an object. This object will then be updated in a hashmap of <key, object>.
So far, I can type in the input areas, but if I add another item in the hashmap which generates another div with the input elements it will contain the same value of the previous inputs.
So I need a way to reset those labels within the form and a way to update the hashmap.
I got an updateMap function, but I don't know where to place it for it to await the changes.
Edit: I need to reset the state of link, but when I do a function for it. It says something about preventing infinite loops.
export default function Admin() {
const [link, setLink] = useState({ name: "", link: "" });
const [links, setLinks] = useState(new Map());
const clickAddLink = () => addToMap(links.size + 1, link);
const deleteOnMap = (key) => {
setLinks((prev) => {
const newState = new Map(prev);
newState.delete(key);
return newState;
});
};
const getName = (key) =>
{
let linkFromKey = links.get(key)
return linkFromKey.name
}
const getLink = (key) =>
{
let linkFromKey = links.get(key)
return linkFromKey.link
}
const addToMap = (key, value) => {
setLinks((prev) => new Map([...prev, [key, value]]));
};
const updateMap = (key, value) => {
setLinks((prev) => new Map([...prev, [key, value]]));
};
const clear = () => {
setLinks((prev) => new Map(prev.clear()));
};
.... skipping code ....
<div>
{console.log(link.name)}
{console.log(link.link)}
{[...links.keys()].map((key) => (
<div key={key}>
{links.size > 0 && (
<div>
<form>
<span>Name</span>
<input
type="text"
placeholder={getName(key)}
required
value={link.name}
onChange={(e) =>
setLink({ name: e.target.value })
}
/>
<span>Link</span>
<input
type="text"
placeholder={getLink(key)}
required
value={link.link}
onChange={(e) =>
setLink({ link: e.target.value })
}
/>
</form>
</div>
)}
</div>
))}
</div>
{console.log(link.name)}
{console.log(link.link)}
</div>
```
Requirement - There will be two input fields in one row, one being name and second is number. In the second row, again two fields will be there with one being name and second is number. Now, the name field is readOnly and the second field number can be edited by user.
First time, the state which is in [{}] format would be empty. Now, when the user enters the number in the first row input, the state should get updated as [{ name: 'user1', number: 123}]. When the user enters value in row 2, the state should get updated as [{ name: 'user1', number: 123}, { name: 'user2', number: 456}]. If the user enters the value in row number 1 again, the existing object value should updated then adding a new one.
For example, if the user1's number gets updated to 789, the state value now should be [{ name: 'user1', number: 789 }, { name: 'user2', number: 456}].
Once this is achieved, if the numbers are duplicate, I should store and error in another state and with state, I can show on UI that the numbers are duplicate and need to have a unique number.
import { useState } from 'react';
const Test = () => {
const [data, setData] = useState([{}]);
const [error, setError] = useState(null);
const handleChange = (number, name) => {
//set data in the state
//validation
//if the values for number are duplicate for both field
//return an error for which input name the value is duplicate
}
return (
<div>
<input name='user1' value='user1' readOnly={true} />
<input name='number1' onChange={(e) => handleChange(e.target.value, 'user1')} />
<hr />
<input name='user2' value='user2' readOnly={true} />
<input name='number2' onChange={(e) => handleChange(e.target.value, 'user2')} />
</div>
)
}
Your handleChange function would look like this. Please not that I changed your initial data state to [] since a new user would simply be inserted instead of updating {}. I also prefer an empty state over null.
First we check whether the name already exists in data array. If not, we append it using curr callback.
If it exists and we only want to update the specific object, we can make use of map function.
By validating before updating the new user we can easily get the user that already has the value with find and set his name inside error state. Plus you can still decide if you want to update the data state if it has duplicate numbers by putting the // update value of exisiting user block inside of an else.
const [data, setData] = useState([]);
const [error, setError] = useState();
const handleChange = (number, name) => {
const idx = data.findIndex((item) => item.name === name);
if (idx === -1) {
// new user insert
setData((curr) => [...curr, { name: name, number: number }]);
} else {
// validation
const duplicateUser = data.find((item) => item.number === number);
if (duplicateUser) {
setError(duplicateUser.name);
console.log(`${duplicateUser.name} and ${name} have duplicate number`);
}
// update value of exisiting user
setData((curr) =>
curr.map((item) => {
return item.name === name ? { name: name, number: number } : item;
})
);
}
};
Look out code
import React, { useState } from "react";
export const Test = () => {
const [data, setData] = useState([]);
const [error, setError] = useState("");
const handleChange = (number, name) => {
setError("");
let isDuplicate = false;
if (!data.length) {
setData([{ name, number }]);
} else if (data.length == 2) {
const newData = data.map((val) => {
if (val.name === name) {
return { name, number };
} else {
isDuplicate = val.number === number;
}
return val;
});
setData(newData);
} else {
const existingData = data.find((val) => val.name === name);
if (existingData) {
setData([{ name, number }]);
} else {
let [user] = data;
if (user.number === number) {
isDuplicate = true;
}
setData((oldState) => {
return [...oldState, { name, number }];
});
}
}
if (isDuplicate) {
setError("Number is duplicate! Each field should have uniq number.");
}
};
return (
<div>
<div className="error">{error}</div>
<input name="user1" value="user1" readOnly={true} />
<input
name="number1"
onChange={(e) => handleChange(e.target.value, "user1")}
/>
<hr />
<input name="user2" value="user2" readOnly={true} />
<input
name="number2"
onChange={(e) => handleChange(e.target.value, "user2")}
/>
</div>
);
};
I hope this will help you. Happy coding...
You have set handleChange event on onChange event so it will call when user enter the single input and check with existing value.
suggestion:- if you want to check whole value which is user enter and compare with the existing value so please use button and click event so it will check when user click after enter the whole value
Hope this will help you. !
import { useState } from "react";
const Test = () => {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const handleChange = (number, name) => {
const exists = data.find((e) => e.name === name);
if (!exists) {
setData([...data, { name, number }]);
} else {
if (exists.number === number) {
setError("Number already exists");
} else {
if (number) {
const index = data.findIndex((e) => e.name === name);
data[index].number = number;
setData([...data]);
}
}
}
};
console.log(data, error);
return (
<div>
<input name="user1" value="user1" readOnly={true} />
<input
name="number1"
onChange={(e) => handleChange(e.target.value, "user1")}
/>
<hr />
<input name="user2" value="user2" readOnly={true} />
<input
name="number2"
onChange={(e) => handleChange(e.target.value, "user2")}
/>
</div>
);
};
This might work for you [Updated]
export const Test = () => {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const handleChange = (number, name) => {
setData((prev) => {
const newState = [...prev];
const index = prev.findIndex((entry) => entry.name === name);
if (index < 0) {
return [
...prev,
{
name,
number
}
];
} else {
const dupEntry = prev.find(
(entry) => entry.name !== name && entry.number === number
);
if (dupEntry) setError(new Error(`${dupEntry.name} already has that value`));
else newState[index].number = number;
}
return newState;
});
//validation
//if the values for number are duplicate for both field
//return an error for which input name the value is duplicate
};
return (
<div>
<input name="user1" value="user1" readOnly={true} />
<input
name="number1"
onChange={(e) => handleChange(e.target.value, "user1")}
/>
<hr />
<input name="user2" value="user2" readOnly={true} />
<input
name="number2"
onChange={(e) => handleChange(e.target.value, "user2")}
/>
</div>
);
};
In this my project, I want the values checked to be displaying in front of the filtered applied but this is not working as expected. Anytime a box is checked, it display all the checked value and anytime a box is uncheck, it goes away automatically. Can someone please help me check what I'm doing wrong. codesandbox
My App.js code
import React from "react";
import "./styles.css";
import { useState, useEffect } from "react";
export default function App() {
const [isselected, setisselected] = useState([]);
const OnChange = (e) => {
console.log(e.target.checked);
const ischecked = e.target.checked;
if (ischecked) {
setisselected([...isselected, e.target.value]);
} else {
const index = isselected.indexOf(e.target.value);
isselected.splice(index, 1);
setisselected(isselected);
}
console.log(isselected);
};
useEffect(() => {
console.log(isselected, "value selected");
}, [isselected]);
return (
<div className="App">
<span>
Filters applied:{" "}
{isselected.map((i) => (
<span>{i}</span>
))}
</span>
<div className="first-search">
<input
type="checkbox"
className="input-1"
value="Lastsevendays"
name="last_seven"
id="1"
onChange={OnChange}
/>
<label htmlFor="">Last 7 days</label>
</div>
<div className="first-search">
<input
type="checkbox"
className="input-1"
name="last24"
value="last_24"
id="2"
onChange={OnChange}
/>
<label htmlFor="">Last 24 hours</label>
</div>
</div>
);
}
Issue
You are mutating the isselected state when removing filters. Array.prototype.splice mutates the array in-place.
const OnChange = (e) => {
const ischecked = e.target.checked;
if (ischecked) {
setisselected([...isselected, e.target.value]);
} else {
const index = isselected.indexOf(e.target.value);
isselected.splice(index, 1); // <-- mutation!!
setisselected(isselected); // <-- same array reference
}
};
Solution
Use Array.prototype.filter to filter by index the isselected array and return a new array reference.
const OnChange = (e) => {
const ischecked = e.target.checked;
if (ischecked) {
setisselected([...isselected, e.target.value]);
} else {
const index = isselected.indexOf(e.target.value);
setisselected((isselected) => isselected.filter((_, i) => i !== index));
}
};
Since the isselected array is storing primitives, you can filter directly by the filter value as well.
setisselected((isselected) =>
isselected.filter((val) => val !== e.target.value)
);
what you're doing is that you are displaying the state with console.log() and it's the nature of console.log() that it shows the previuos value of the state,and also I did some changes in your Onchange function and it's working now try this and then let me know it's working or not:
const OnChange = (e) => {
console.log(e.target.checked);
const ischecked = e.target.checked;
if (ischecked) {
setisselected([...isselected, e.target.value]);
} else {
let temp = [isselected];
const index = temp.indexOf(e.target.value);
temp.splice(index, 1);
setisselected(temp);
}
console.log(isselected);
};
I am having a problem filtering data from an API based on their regularPrice. So the error I am having is kinda stupid. It is 'regular price is not defined no-undef The error is showing on the line where I am passing values to the ContextPrivider. I might be blind. I would really appreciate some help. Thank you very much.
Book Context
import React, {useState, useEffect} from 'react'
import URL from '../utilis/URL';
const BookContext = React.createContext();
export default function BooksProvider({ children }) {
const [data, setData] = useState([])
const [filters, setFilters]= useState({
regularPrice:"",
length:""
})
/*fetching data */
const fetchData = async () => {
const response = await fetch(URL);
const result = await response.json();
console.log(result)
setData(result);
};
useEffect(()=>{
fetchData();
},[])
const updateFilters = e => {
const type = e.target.type;
const filter = e.target.name;
const value = e.target.value;
let filterValue;
if (type === "checkbox") {
filterValue = e.target.checked;
} else if (type === "radio") {
value === "all" ? (filterValue = value) : (filterValue = parseInt(value));
} else {
filterValue = value;
}
setFilters({ ...filters, [filter]: filterValue });
};
/* filtering price books */
React.useLayoutEffect(() => {
let newBooks = [...data].sort((a, b) => a.regularPrice - b.regularPrice);
const { regularPrice } = filters;
if (regularPrice !== "all") {
newBooks = newBooks.filter(item => {
if (regularPrice === 0) {
return item.regularPrice <10;
} else if (regularPrice === 10) {
return item.regularPrice > 10 && item.regularPrice < 20;
} else {
return item.regularPrice > 20;
}
});
}
}, [filters, data]);
return (
<BookContext.Provider value={{ data, filters, regularPrice, updateFilters, handleSelectCategory, setCurrentSelectedCategory, currentSelectedCategory }}>
{children}
</BookContext.Provider>
);
}
export {BookContext, BooksProvider}
Filters
import React, { useContext } from 'react'
import { BookContext } from '../../context/books'
const Filters = () => {
const {filters:{regularPrice, updateFilters}}= useContext(BookContext)
return (
<div>
<p>Regular Price</p>
<label>
<input
type="radio"
name="regularPrice"
id="all"
value="all"
checked={regularPrice === "all"}
onChange={updateFilters}
/>
all
</label>
<label>
<input
type="radio"
name="regularPrice"
value="0"
checked={regularPrice === 0}
onChange={updateFilters}
/>
$0 - $10
</label>
<label>
<input
type="radio"
name="regularPrice"
value="10"
checked={regularPrice === 10}
onChange={updateFilters}
/>
$10 - $20
</label>
<label>
<input
type="radio"
name="regularPrice"
value="20"
checked={regularPrice === 20}
onChange={updateFilters}
/>
Over $20
</label>
</div>
)
}
export default Filters
As the error says, regularPrice isn't defined in BooksProvider. You don't have to expose regularPrice since you can get it from filters but if you really want to, destructure it from filters.
const { regularPrice } = filters
return (
<BookContext.Provider
value={{
data,
filters,
regularPrice,
updateFilters,
handleSelectCategory,
setCurrentSelectedCategory,
currentSelectedCategory,
}}
>
{children}
</BookContext.Provider>
)
I have simple fields. I want to populate the input boxes based on the data that I get from axios response. See below image
I am using hooks, to save data to state.
My question is how I am able to populate input boxes upon clicking get details button (response from api).
see below codes
const [data, setData] = useState([])
const [advisoryDetails, setadvisoryDetails] = useState({
ADVISORYID: '',
ADVISORYDESC: '',
CREATEDBY:'',
MODIFIEDBY:'',
STATUS1: ''
})
const [advisoryDetails1, setadvisoryDetails1] = useState([])
const [advisoryID, setadvisoryID] = useState('')
const getAdvisoryTest = async () => {
await axios.post('/API',advisoryID)
.then(response => {
console.log(response.data)
setData(response.data)
console.log('data',data)
setadvisoryDetails1(response.data)
console.log('advisoryDetails1',advisoryDetails1)
alert('success')
})
advisoryDetails1.map(adv => {
advisoryDetails.ADVISORYID = adv.ADVISORYID;
advisoryDetails.ADVISORYDESC = adv.ADVISORYDESC;
advisoryDetails.CREATEDBY = adv.CREATEDBY;
advisoryDetails.MODIFIEDBY = adv.MODIFIEDBY;
if(adv.CREATEDBY && adv.MODIFIEDBY != '')
{
advisoryDetails.STATUS1 = 'Modified'
}
else{ advisoryDetails.STATUS1 = 'New'}
console.log('populate',advisoryDetails)
})
}
const txtAdvIdOnChange = e =>{
setadvisoryID(prevState =>({
...prevState,
'ADVISORYID':e.target.value
}));
console.log('onChange ID:',advisoryID)
}
return(
<div>
<label>AdvisoryID: </label>
<input type='text' placeholder='Advisory ID' className='txtAdvisoryID' onChange={txtAdvIdOnChange} />
<button onClick={()=>getAdvisoryTest()}>Get Details</button>
<br /><br />
<label>Advisory Desc: </label>
<input type='text' placeholder='textbox1' className='txtAdvisoryDesc' value={advisoryDetails&&advisoryDetails.ADVISORYDESC} disabled/>
<br/>
<label>New / Modified: </label>
<input type='text' placeholder='textbox2' className='txtStatus' value={advisoryDetails&&advisoryDetails.STATUS1} disabled/>
</div>)
On those codes input boxes was not populated, even in console.log
Hope you can help me thank you.
I think you can refactor your code like below.
I've moved set setadvisoryDetails to within .then() of axios because you're using the same data and you're don't have to go through the loop if you just want the last iteration's value. And in the inputs you don't have to check if advisoryDetails exists or has non empty value because you've initialized it in const [advisoryDetails, setadvisoryDetails] = useState({...})
const App = (props) => {
const [data, setData] = useState([])
const [advisoryDetails, setadvisoryDetails] = useState({
ADVISORYID: '',
ADVISORYDESC: '',
CREATEDBY: '',
MODIFIEDBY: '',
STATUS1: ''
})
const [advisoryDetails1, setadvisoryDetails1] = useState([])
const [advisoryID, setadvisoryID] = useState('')
const getAdvisoryTest = () => {
axios.post('/API', advisoryID)
.then(response => {
const respData = response.data;
setData(respData)
setadvisoryDetails1(respData)
console.log({
respData, data, advisoryDetails1
});
alert('success');
if (respData.length > 0) {
const adv = respData[respData.length - 1];
setadvisoryDetails((prevState) => ({
...prevState,
...adv,
STATUS1: adv.CREATEDBY && adv.MODIFIEDBY != '' ? 'Modified' : 'New'
}))
}
})
}
const txtAdvIdOnChange = e => {
setadvisoryID(prevState => ({
...prevState,
'ADVISORYID': e.target.value
}));
console.log('onChange ID:', advisoryID)
}
return (
<div>
<label>AdvisoryID: </label>
<input type='text' placeholder='Advisory ID' className='txtAdvisoryID' onChange={txtAdvIdOnChange} />
{/* If you're just passing a function without any param or event params, then just pass the function name like a variable */}
<button onClick={getAdvisoryTest}>Get Details</button>
<br /><br />
<label>Advisory Desc: </label>
<input type='text' placeholder='textbox1' className='txtAdvisoryDesc' value={advisoryDetails.ADVISORYDESC} disabled />
<br />
<label>New / Modified: </label>
<input type='text' placeholder='textbox2' className='txtStatus' value={advisoryDetails.STATUS1} disabled />
</div>
);
}
when you click the get details button your state was not updating that was the issue(value only change when the state was updated otherwise it is not updated)
//before don't do like this
advisoryDetails1.map(adv => {
advisoryDetails.ADVISORYID = adv.ADVISORYID;
advisoryDetails.ADVISORYDESC = adv.ADVISORYDESC;
advisoryDetails.CREATEDBY = adv.CREATEDBY;
advisoryDetails.MODIFIEDBY = adv.MODIFIEDBY;
if(adv.CREATEDBY && adv.MODIFIEDBY != '')
{
advisoryDetails.STATUS1 = 'Modified'
}
else{ advisoryDetails.STATUS1 = 'New'}
console.log('populate',advisoryDetails)
})
//after(only last element of advisoryDetails1 array was updated in the state)
advisoryDetails1.forEach(adv => {
let STATUS1 ='New'
if(adv.CREATEDBY && adv.MODIFIEDBY != '')
{
STATUS1 = 'Modified'
}
setadvisoryDetails({ADVISORYID : adv.ADVISORYID,
ADVISORYDESC:adv.ADVISORYDESC,
CREATEDBY:adv.CREATEDBY,
MODIFIEDBY:adv.MODIFIEDBY,
STATUS1:STATUS1
})
})
if you want to view your last element in your advisoryDetails1 array do like this
let adv=advisoryDetails1[advisoryDetails1.length -1];
let STATUS1 ='New'
if(adv.CREATEDBY && adv.MODIFIEDBY != '')
{
STATUS1 = 'Modified'
}
setadvisoryDetails({ADVISORYID : adv.ADVISORYID,
ADVISORYDESC:adv.ADVISORYDESC,
CREATEDBY:adv.CREATEDBY,
MODIFIEDBY:adv.MODIFIEDBY,
STATUS1:STATUS1
})
//it update your advisoryDetails state when advisoryDetails1 changed
useEffect(()=>{
advisoryDetails1.forEach(adv => {
let STATUS1 ='New'
if(adv.CREATEDBY && adv.MODIFIEDBY != '')
{
STATUS1 = 'Modified'
}
setadvisoryDetails({ADVISORYID : adv.ADVISORYID,
ADVISORYDESC:adv.ADVISORYDESC,
CREATEDBY:adv.CREATEDBY,
MODIFIEDBY:adv.MODIFIEDBY,
STATUS1:STATUS1
})
})
},[advisoryDetails1]);
//check whether advisoryDetails is changed or not
useEffect(()=>{
console.log('populate',advisoryDetails)
},[advisoryDetails])
everything fine but doesn't do like this only the last element of the array was updated so you must need some changes based on your requirements
On your input value properties try adding a space either side of the &&.
<input type='text' placeholder='textbox2' className='txtStatus' value={advisoryDetails.STATUS1 && advisoryDetails.STATUS1} disabled/>