changing some specific value react usestate - reactjs

const [checkedHealth, setCheckedHealth] = useState(checkboxHealthLabels);
const handleChangeHealth = (event) => {
setCheckedHealth([
...checkedHealth,
[event.target.name]: event.target.checked,
]);
};
and checkboxHealthLabels file :
export const checkboxHealthLabels = [
{ name: "Alcohol-Free", checked: false },
{ name: "Celery-Free", checked: false },
{ name: "Dairy-Free", checked: false },
];
now I want to change just one object for example : { name: "Alcohol-Free", checked: false },
and other values have to stay same. How can I do that?

Find the index of the object in the array with the same name, then toggle it as needed:
const handleChangeHealth = ({ target }) => {
const { name } = target;
const index = checkedHealth.findIndex(obj => obj.name === name);
setCheckedHealth([
...checkedHealth.slice(0, index),
{ name, checked: target.checked },
...checkedHealth.slice(index + 1)
]);
};
You could also consider having the state be an object (with the names being the object properties) instead of an array, it might be easier.

Related

react-awesome-query-builder empty select when click outside select

I use asyncFetch and I select value from SelectWiget , after any click outside make empty value this select.
I use next config for fields
products: {
type: '!struct',
label: 'Products',
subfields: {
title: {
type: 'select',
label: 'Name',
fieldSettings: {
asyncFetch: async (search, offset) => {
const toSearch = _.isEmpty(search) ? 'null' : search;
const prodApi = (await axios.get(`http://localhost:8002/api/products/1/${toSearch}?offset=${offset}`, { headers: authHeader() }));
const productsValues = prodApi.data.data.map(
product => ({
title: product.title,
value: product.id
})
)
return {
values: productsValues,
hasMore: true,
}
},
useAsyncSearch: true,
useLoadMore: true,
forceAsyncSearch: false,
allowCustomValues: false
}
},
Another error is when i select some value the asyncFetch is call again.
Is this a bug in that package or i have missing some configuration ?
the package what i use is react-awesome-query-builder
the value from result of function async need to be string, so function is like :
asyncFetch: async (search, offset) => {
const toSearch = _.isEmpty(search) ? 'null' : search;
const prodApi = (await axios.get(`http://localhost:8002/api/products/1/${toSearch}?offset=${offset}`, { headers: authHeader() }));
const productsValues = prodApi.data.data.map(
product => ({
title: product.title,
value: product.id.toString(),
})
)
return {
values: productsValues,
hasMore: true,
}
}

How can I delete an item inside a nested array with Hooks?

I am trying to remove a single item from state inside a nested array, but i am really struggling to understand how.
My data looks as follows, and I'm trying to remove one of the 'variants' objects on click.
const MYDATA = {
id: '0001',
title: 'A good title',
items: [
{
itemid: 0,
title: 'Cheddar',
variants: [
{ id: '062518', grams: 200, price: 3.00},
{ id: '071928', grams: 400, price: 5.50},
]
},
{
itemid: 1,
title: 'Edam',
variants: [
{ id: '183038', grams: 220, price: 2.50},
{ id: '194846', grams: 460, price: 4.99},
]
},
{
itemid: 2,
title: 'Red Leicester',
variants: [
{ id: '293834', grams: 420, price: 4.25},
{ id: '293837', grams: 660, price: 5.99},
]
}
]
}
Against each variant is a button which calls a remove function, which (should) remove the deleted item and update the state. However, this is not happening and I'm not sure what I am doing wrong.
const [myCheeses, setMyCheeses] = useState(MYDATA);
const removeMyCheese = (variantID, itemindx) => {
console.log(variantID);
setMyCheeses((prev) => {
const items = myCheeses.items[itemindx].variants.filter(
(variant) => variant.id !== variantID
);
console.log(items, itemindx);
return {
...myCheeses.items[itemindx].variants,
items
};
});
};
An example of the issue I'm facing can be seen here
https://codesandbox.io/s/funny-dan-c84cr?file=/src/App.js
Any help would be truly appreciated.
The issue is that, setMyCheeses function not returning the previous state including your change(removal)
Try one of these functions;
1st way
const removeMyCheese = (variantID, itemindx) => {
setMyCheeses((prev) => {
const items = myCheeses.items[itemindx].variants.filter(
(variant) => variant.id !== variantID
);
const newState = prev;
newState.items[itemindx].variants = items;
return {...newState};
});
};
https://codesandbox.io/s/bold-worker-b12x1?file=/src/App.js
2nd way
const removeMyCheese = (variantID, itemindx) => {
setMyCheeses((prev) => {
const items = myCheeses.items.map((item, index) => {
if (itemindx === index) {
return {
...item,
variants: item.variants.filter(
(variant) => variant.id !== variantID
)
};
} else {
return item;
}
});
return { ...prev, items: items };
});
};
https://codesandbox.io/s/sharp-forest-qhhwd
try this function, it's work for me :
const removeMyCheese = (variantID, itemindx) => {
//console.log(variantID);
const newMyCheeses = myCheeses;
const newItems = newMyCheeses.items.map((item) => {
return {
...item,
variants: item.variants.filter((variant) => variant.id !== variantID)
};
});
setMyCheeses({ ...newMyCheeses, items: newItems });
};
https://codesandbox.io/s/jolly-greider-fck6p?file=/src/App.js
Or, you can do somthing like this if you don't like to use the map function :
const removeMyCheese = (variantID, itemindx) => {
//console.log(variantID);
const newMyCheeses = myCheeses;
const newVariants = newMyCheeses.items[itemindx].variants.filter(
(variant) => variant.id !== variantID
);
newMyCheeses.items[itemindx].variants = newVariants;
setMyCheeses({ ...newMyCheeses });
};

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

How to map trough array of object and toggle boolean property selected

I have state in React functional component. It is and array of objects. Every object in that collection has property "selected", which is a boolean. That array looks like this:
const [filterOptions, setFilterOptions] = useState([
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]);
After handleFilter func is executed I need to set state so this array has same title properties but reverse (toggle) selected properties.
This is handleFilter func in which I need to toggle every selected property of array objects:
const handleFilter = () => {
setFilterOptions();
};
function App() {
const [filterOptions, setFilterOptions] = useState([
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]);
const handleFilter = (e) => {
let newArr = [...filterOptions];
let value = e.target.value;
if (value === "lowest") {
newArr[0].selected = true;
newArr[1].selected = false;
} else if (value === "highest") {
newArr[0].selected = false;
newArr[1].selected = true;
}
setFilterOptions(newArr)
};
return (
<div>
<select onChange={handleFilter}>
<option value="lowest">a</option>
<option value="highest">b</option>
</select>
{console.log((filterOptions))}
</div>
);
}
please check hope it will work
var arryObj =[
{
title: 'highest',
selected: true,
},
{
title: 'lowest',
selected: false,
},
]
const handleFilter = (index,value) => {
arryObj[index].selected = value
};
handleFilter(0,false)
console.log(arryObj)
handleFilter(1,true)
console.log(arryObj)
You can pass a function into setFilterOptions to change the state based on the previous state.
const handleFilter = () => {
setFilterOptions(prevState =>
prevState.map(obj => ({...obj, selected: !obj.selected}))
);
};

map over multiple arrays and only return specific ones

I currently have an axios get request that fetches data from a nasa API and returns it into a list of arrays.
getDataHandler= () => {
axios.get('https://api.nasa.gov/neo/rest/v1/neo/browse?api_key=DEMO_KEY',)
.then((response) => {
const restructuredData = response.data.near_earth_objects.map(
({ name, estimated_diameter, close_approach_data }) => {
const close_approaches = close_approach_data && close_approach_data.length
? close_approach_data.map(({ orbiting_body }) => orbiting_body)
: ["no orbited planet"] // If the array doesn't exist, just use an empty array.
return [
name,
estimated_diameter.kilometers.estimated_diameter_min,
estimated_diameter.kilometers.estimated_diameter_max,
close_approaches[0]
]
})
})
It returns a list of arrays that look like this:
0: (4) ["21277 (1996 TO5)", 1.6016033798, 3.5812940302, "Mars"]
1: (4) ["162038 (1996 DH)", 1.2721987854, 2.844722965, "no orbited planet"]
2: (4) ["189058 (2000 UT16)", 1.332155667, 2.978790628, "Earth"]
3: (4) ["276274 (2002 SS41)", 0.9650614696, 2.1579430484, "Earth"]
4: (4) ["322913 (2002 CM1)", 1.214940408, 2.7166893409, "Jupiter"]
5: (4) ["435730 (2008 UK90)", 0.4411182, 0.9863702813, "no orbited planet"]
Then it gets the list and setState it.
Problem is I have a dropDown menu to only show data from specific planets. So I was wondering if it's possible to map of it again and only keep the ones that are equal to the current selected planet.
And if no planets are selected return all of them.
code i have so far
class MainPage extends Component {
state = {
data: [['name', 'min estimated diameter', 'max estimated diameter', { role: "planet" }]],
dropDownOptions: [
{ value: 'all', label: 'All' },
{ value: 'earth', label: 'Earth' },
{ value: 'mars', label: 'Mars' },
{ value: 'mercury', label: 'Mercury' },
{ value: 'venus', label: 'Venus' },
{ value: 'saturn', label: 'Saturn' },
{ value: 'jupiter', label: 'Jupiter' },
{ value: 'no orbited planet', label: 'No orbited planet'}
],
SelectedDropDownOption: { value: 'all', label: 'All' },
}
componentDidMount() {
this.getDataHandler()
}
getDataHandler= () => {
axios.get('https://api.nasa.gov/neo/rest/v1/neo/browse?api_key=DEMO_KEY',)
.then((response) => {
const restructuredData = response.data.near_earth_objects.map(
({ name, estimated_diameter, close_approach_data }) => {
const close_approaches = close_approach_data &&
close_approach_data.length
? close_approach_data.map(({ orbiting_body }) => orbiting_body)
: ["no orbited planet"]
return [
name,
estimated_diameter.kilometers.estimated_diameter_min,
estimated_diameter.kilometers.estimated_diameter_max,
close_approaches[0]
]
}
)
const joined = this.state.data.concat(restructuredData)
this.setState({ data: joined })
})
.catch(function (error) {
console.log(error);
})
}
DropDownChangeHandler= (SelectedDropDownOption) => {
console.log("hello")
this.setState({SelectedDropDownOption});
}
render () {
console.log(this.state.data)
console.log(this.state.SelectedDropDownOption)
console.log(this.state.SelectedDropDownOption.value)
return (
<React.Fragment>
<DropDown options={this.state.dropDownOptions} onChange={this.getPlanetInformation}/>
<Chart chartData={this.state.data} />
</React.Fragment>
);
}
}
export default MainPage;
You can use filter method to achieve your goal. You loop over every sub array and you keep only those which includes the require planet name passed as function parameter.
const arrayList = [["21277 (1996 TO5)", 1.6016033798, 3.5812940302, "Mars"], ["162038 (1996 DH)", 1.2721987854, 2.844722965, "no orbited planet"], ["189058 (2000 UT16)", 1.332155667, 2.978790628, "Earth"],["276274 (2002 SS41)", 0.9650614696, 2.1579430484, "Earth"], ["322913 (2002 CM1)", 1.214940408, 2.7166893409, "Jupiter"]]
const getPlanetInformation = (planet) => {
const information = arrayList.filter(item => item.includes(planet))
console.log(information)
return information.length ? information : arrayList
}
If there is no planet selected from your dropdown value or the selected doesn't exists inside your array, you can just return the initial value.

Resources