Add dynamic key to set state, react - javascript

I have this state
this.state = {
dropdown1: false,
dropdown2: false,
dropdown3: false
}
I want to access to these dropdowns in state using this.setState but the number after 'dropdown' comes from API
onMaca = (ev) => {
this.setState({
dropdown + ev: true
})
}
So I want the key to be dynamic 'dropdown1' for example.
Thanks for your answers

you can access the object property like this object['property name']
onMaca = (ev) => {
this.state['dropdown' + ev]= true;
this.setState({
...this.state
})
}

https://codezup.com/add-dynamic-key-to-object-property-in-javascript/
You can use any of these to set key dynamically. I will try to update the answer with an example in a while for setState.

The state is a JS object, so you can get its keys as usual, like so:
const stateKeys = this.state.keys()
Now you have an array: [ "dropdown1", "dropdown1", "dropdown1" ]
One way to use it would be:
const keysMap = statekeys.map(( item, i ) => return {
key: item,
idx: i,
number: item.replace( /dropdown/, '' )
}
keysMap will look like so: [ { key: 'dropdown1', idx: 0, number "1" }, { key: 'dropdown1', idx: 1, number "2" }, { key: 'dropdown1', idx: 2, number "3" } ]
You can query keysMap for a given dropDownNumber like so:
let k = keysMap.find( kmap => kmap.key = dropDownNumber )
To set the dropdown's state:
this.setState({ k: <whatever> })

Related

react native: passing the value of selected items to a function

I am trying to implement a MultipleSelectList from this library react-native-dropdown-select-list. And I am saving the selected items in the AsyncStorage of #react-native-async-storage/async-storage. I implemented 2 useState variables: first const [selected, setSelected] = useState([{}]); this is a list of {name : muscleGroup, value : id(s) of the exercise(s)} (this is the object that is saved in the list of viewDataList [see setExerciseViewLists ]). The other useState variable that I have implemented is:
const [selectedCount, setSelectedCount] = useState({ // a list of the number of selected exercises per muscleGroup
Back: 0,
Legs: 0,
Chest: 0,
.....
});
The idea: when I call handleSelect (see handleSelect) from my MultipleSelectList I give it 2 parameters (val and item) val should be the id(s) of the exercise(s) because I defined it as this in my MultipleSelectList (see MultipleSelectList) and the item is one list item from viewDataList.
The problem is: val is for some reason, not an ID or a list of IDs, it is an anonymous function:
function (val) {
var temp = _babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0___default()(new
Set([].concat(_babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_0___default()(val),
[value])));
return temp;
}
(I don't really understand what this is). Any help would be appriciated.
MultipleSelectList
{viewDataList.map((item, index) => (
<MultipleSelectList
data={item.list.map(listItem => ({
value: listItem.id, //here
}))}
save="value"
setSelected={(val) => handleSelect(val, item)}
selected={selected.map(item => item.value)}
/>
))}
handleSelect/Async saving
const handleSelect = async (val, item) => {
setSelectedCount(prevState => ({
...prevState,
[item.name]: prevState[item.name] + 1, //item.name = muscleGroup
}));
setSelected(prevState => ([
...prevState,
{
name: item.name,
value: val //val should be the ID(s)
},
]));
try {
await AsyncStorage.setItem('selected', JSON.stringify([
...selected,
{
name: item.name,
value: val,
}
]));
await AsyncStorage.setItem('selectedCount', JSON.stringify({
...selectedCount,
[item.name]: selectedCount[item.name] + 1,
}));
} catch (e) {
console.e('Error saving data to AsyncStorage:', e);
}
};
setExerciseViewLists
const setExerciseViewLists = () => {
let list = [];
list.push(
{.....},
{
num: 3,
name: "muscleGroup",
list: [{.....}, { id: "123", exercises : "somthing" }, {.....}]
},
{.....},
);
setViewDataList(list);
};
The issue is not with your data but with the setSelected prop for which you are sending a custom function handleSelect. But if you see the library's documentation, it is clearly mentioned like setSelected is for For Setting the option value which will be stored in your local state. So you can only store the selected values in the local state. The code should be like below inside the component.
const [selectedValues, setSelectedValues] = useState();
return <MultipleSelectList
data={SOME_DATA}
save="value"
setSelected={setSelectedValues}
/>

How do i setState with hooks of an object which has multiple arrays ? How to setdebtors here?

I am deleting an one id in an array, how do I setState after filtering it here?
https://codesandbox.io/s/react-example-1m2qn
const Debtors = () => {
const debtors = [
{
id: 1,
name: "John",
relation: "friend",
statement: [
{ id: 1, date: 2010, amount: "1000", purpose: "John" },
{ id: 2, date: 2014, amount: "2000", purpose: "john" }
]
},
,
{
id: 2,
name: "Jack",
relation: "Friend",
statement: [
{ id: 1, date: 2010, amount: "1000", purpose: "jack" },
{ id: 2, date: 2014, amount: "2000", purpose: "jack" }
]
}
];
const [newDebtors, setdebtors] = React.useState(debtors);
const handleDelete = (stat, i) => {
const newList = newDebtors[0].statement.filter(x => x.id !== stat.id);
// How to set debtors here ?
// setdebtors({ ...newDebtors, statement[0]: newList });
console.log(newList)
// How to set debtors here ?
There's two problems:
1) You are iterating off the original debtors object in your render, instead of the newDebtors state you created via useState(), which is why there does not appear to be any UI change.
You need: newDebtors[0].statement.map
2) You need to pass in the item index in your handleDelete() so it knows what item in the array to update. You can have the function do something like this:
In the onClick:
<a
href="javascript:;"
onClick={() => handleDelete(stat, i, 0)}
>
In the handleDelete():
const handleDelete = (stat, i, arrayIndex) => {
const updatedDebtors = newDebtors.map((item, index) => {
if (index === arrayIndex) {
return {
...item,
statement: item.statement.filter(
statement => statement.id !== stat.id
)
};
} else {
return item;
}
});
setDebtors(updatedDebtors);
};
See sandbox for full solution: https://codesandbox.io/s/react-example-x7uoh
You should do it like that:
setdebtors((prevState) => {
let newArray = Array.from(prevState); // Copy the array
// Manipulate the array as you wish
return newArray; // return it
});
The problem is you are mutating the array of "debtors" you need to map through the array of debtors and change any properties in the object.
const handleDelete = (stat, i) => {
const newList = newDebtors.map((debtor, i) => {
if (i === 0) {
debtor.statement = debtor.statement.filter(x => x.id !== stat.id);
}
return debtor;
});
setdebtors(newList);};
An even better approach is to use "useReducer" which is used for mutating more complex pieces of state, like you have here. THe docs are very helpful useReducer
Hmm I dont know what exactly you are trying to do,
Is this what you are looking for?
const handleDelete = (stat, i) => {
const newList = newDebtors[0].statement.filter(x => x.id !== stat.id);
const newFirstItem = {...newDebtors[0],statement: newList}
const newDebtorList = newDebtors.filter(x => x.id !== newFirstItem.id);
newDebtorList.unshift(newFirstItem);
setdebtors(newDebtorList);
}
I know this seems complex but you kinda actually need to do this as you cannot mutate an array in the state...
What I did here is I first created a new statement list(newList), then created a newFirstItem to be set as the new newDebtors[0], then created a new array(newDebtorList) of all the elements of newDebtors except the first one, I modified this array by pushing the newFirstItem to the 0th position(using unshift)
Finally updated the state with this new array...
hope it helps :)
Note: this is for changing the 0th element if you have the id please change the code accordingly

Insert an item in a list inside another in react state

I have this variable on my state:
this.state = {
itemList: {
item: [
{
itemAlias: [
{
name: null
}
],
idItem: null,
itemName: null,
}
]
}
}
what I want to do is to insert a new item alias on a cetain item.I did this and its inserting a new itemAlias to my item but is also creates a new Item, and I dont want that, I just want to update my item with a new Item alias:
insertAliasToList = (itm) => {
let insertedAlias = {
name: 'test'
}
itm.itemAlias.push(insertedAlias)
this.setState(prevState => ({
...prevState,
itemList: {
...prevState.itemList,
item: [...prevState.itemList.item, p]
}
}))
}
I have also tried this but the error Uncaught TypeError: Invalid attempt to spread non-iterable instance appears.
insertAliasToList = (itm) => {
let insertedAlias = {
name: 'test'
}
// itm.itemAlias.push(insertedAlias)
this.setState(prevState => ({
...prevState,
itemList: {
...prevState.itemList,
item: {
...prevState.itemLIst.item,
itemAlias:[...prevState.itemList.item.itemAlias,insertedAlias]
}
}
}))
}
Thanks in advance!
Since you are trying to replace the contents of an item within an array, you will need to first duplicate the array. Then replace what you need within that item using the index and set the state again with the new array.
let insertedAlias = {
name: 'test'
}
// Duplicate 'item' array
const item = [...this.state.itemList.item]
// Change item using index '0' on the duplicated array
item[0] = {
...item[0],
itemAlias: [
...item[0].itemAlias,
insertedAlias,
]
}
// Set state with new item array that contains changes you made
this.setState(prevState => ({
...prevState,
itemList: {
...prevState.itemList,
item: [
...item,
]
}
}))
You can do something like this:
insertAliasToList = (itm) => {
let insertedAlias = {
name: 'test'
}
this.setState(prevState => ({
...prevState,
itemList: {
...prevState.itemList,
item: prevState.itemList.item.map(i => {
if (i.idItem === itm.idItem) {
return {
...i,
itemAlias: [...i.itemAlias, insertedAlias]
}
}
return i;
})
}
}))
}
It will only work if idItem is unique.

React/JavaScript: Using React spread to performing 2 changes(at the same time) on array

I am trying to rename 'timestamp' in my array with key/value pairs. Currently, I am adding id and assinging a value but I also need to change 'timestamp' to 'start'. Is it possible to do all that at once?
Here is what I have so far:
const { data } = this.state
const newAction = data.action.map((actionItem, index) => ({
...actionItem,
id: index + 1,
...actionItem,
'start': actionItem.timestamp
}));
const items = {
...data,
action: newAction
};
Data Structure:
Update:
So, instead of replacing timestamp with start, the code above adds start to array. I want timestamp to be named start.
Another approach with spread syntax.
const action = [
{
Second: [],
action: "Program Executed",
timestamp: 12345,
},
{
Second: [],
action: "Something Happened",
timestamp: 67891,
}
];
const newAction = action.map( ( actionItem, index ) => {
const { timestamp: start, ...rest } = actionItem;
return {
...rest,
id: index + 1,
start,
}
});
console.log( newAction );

How do I update an array of objects in component state?

I am trying to update the property of an object which is stored in an array.
my state looks something like this:
state = {
todos: [
{
id: '1',
title: 'first item,
completed: false
},
{
id: '2',
title: 'second item,
completed: false
}
],
}
What I am trying to do is access the second element in the 'todos' array and update the completed property to either false -> true or true -> false.
I have a button with the handler for update, and my class method for the update looks like this:
onUpdate = (id) => {
const { todos } = this.state;
let i = todos.findIndex(todo => todo.id === id);
let status = todos[i].completed
let updatedTodo = {
...todos[i],
completed: !status
}
this.setState({
todos: [
...todos.slice(0, i),
updatedTodo,
...todos.slice(i + 1)
]
});
}
While this does work, I want to find out if there is a more concise way of achieving the same result; I tried to use Object.assign(), but that didn't work out because my 'todos' is an array, not an object. Please enlighten me with better code!
It would be best to use update function to make sure you don't work on outdated data:
onUpdate = (id) => {
this.setState(prevState => {
const copy = [...prevState.todos];
const index = copy.findIndex(t => t.id === id);
copy[index].completed = !copy[index].completed;
return { todos: copy }
})
}
You can simply copy your todos from state, then make edits, and after that put it back to the state
onUpdate = (id) => {
var todos = [...this.state.todos]
var target = todos.find(todo => todo.id == id)
if (target) {
target.completed = !target.completed
this.setState({ todos })
}
}

Categories

Resources