How do I use useState hook to update an array within an array of objects that's dependent on an array index?
end goal data:
foodData = [
{
foodId: 'fdsafsdafsa',
fruitsArray: ['banana', 'orange']
},
{
foodId: '234243fdsfdsafsasdf343432afsdafsa',
fruitsArray: ['apple']
},
{
foodId: 'fdsafsdafsa',
fruitsArray: ['strawberry', 'orange']
},
]
I have a function with arguments, (fruits, fruitIndex, foodIndex, foodId)
const logFruitsIntoFoodData = (fruits, fruitIndex, foodIndex, foodId) => {
// update state here...
const foodToUpdate = {...foodData};
foodToUpdate[foodIndex] = {
...foodData[foodIndex],
// This gets overwritten,
// how do I continue to add or
// update the fruit based on fruit index?
['fruitsArray']: fruits,
};
}
I'm trying to update/add fruits into the fruitsArray so when the function gets invoked, it'll insert the proper fruits into foodData, or it'll update it, depending on what the fruitIndex is.
Should I be using two separate useState:
const [foodData, setFoodData]= useState([]);
const [fruitsArray, setFruitsArray] = useState([]);
where I should get the array of fruits first, then add it into foodData, or can I just have 1 useState to deal with everything?
I'm not sure I'd use an array, since you have foodId, meaning you could easily convert foodData into an object. I'd say if you're using an array then use the index. If you want to use foodId to identify the item you're updating, then just make foodData an object.
It's a little bit cumbersome but this is what I think you're trying to do:
const logFruitsIntoFoodData = (fruit, fruitIndex, foodIndex) => {
// isolate the food you're updating and clone it
const foodToUpdate = { ...foodData[foodIndex] };
// replace fruit in the specified index...
foodToUpdate.fruitsArray.splice(fruitIndex, 1, fruit);
// clone existing state
const newFoodDataState = [...foodData];
// replace the item in the specified
newFoodDataState.splice(foodIndex, 1, foodToUpdate);
// set new state
setFoodData(newFoodDataState);
};
I'm not 100% sure this is what you meant. Let me know and I can modify accordingly!
I was able to get it to work when I did it like this:
const [foodData, setFoodData]= useState([]);
const logFruitsIntoFoodData = (fruits, fruitIndex, foodIndex, foodId) => {
// checks if the index exists in the array.
if (!foodData[foodIndex]) {
foodData[foodIndex] = {
foodId,
fruitsArray: [],
};
setFoodData({...foodData});
}
// now I can insert fruits depending on the fruitIndex
foodId[foodIndex].fruitsArray[fruitIndex] = fruits;
}
Not sure if this is the best method, but it works. If anyone has a better way, I'd love to hear it!
Related
After executing the newData[0].id = newValue I am actually updating the react initialData state. How is that possible?
Is my understanding that filter should return a new array different than the original one, also I am not ussing the setState feature so I don't understand why the state is changing.
Because arrays are mutable. it will keep the reference to the original array even after filtering.
use the spread operator to avoid mutating the original array
const data = [...newData]
data[0].id = newValue
As per the new beta docs on updating items in array
setInitialData(prev => {
// create a new array
const withReplaced = prev.map(elem => {
if (elem.id === id) {
const newVal = //set new value
// create a new updated element
return {
...elem,
id: newVal
}
} else {
// The rest haven't changed
return elem;
}
});
return withReplaced;
})
Hope it helps
you can't update the initialData,but the you can update the son of the array.And if you don't use "setData".The views won't change.
Hi all,
I have a small react app that is creating (mapping from an array) new tabs (and panels) when there is a new message over the websocket.
There is an initial setup, that is hardcoded for the test purposes which sets up 2 tabs on load, any new ones should be appended to these two.
const INITIAL_ARRAY= [
{
id: 1,
child_component_config: {...}
},
{
id: 2,
child_component_config: {...}
}
];
const template = {
child_component_config: {...}
}
The code, simplifed:
const [current_array, setNewArray] = useState( INITIAL_ARRAY);
export default function ParentComponent() {
useEffect(() => {
const client = new ws...
client.onConnect = function (frame) {
var message = clientDesk.subscribe( '/topic/desks/4', function (message) {
// on message
var new_tab = TEMPLATE;
new_tab.id = Math.max( ...current_array.map( elem => elem.id ) ) + 1;
setActiveTab(new_tab.id);
setNewArray([...current_array, new_tab]);
}
}
}, [ current_array ]);
const tabs = map tabs
cosnt panels = map panels
return(
{tabs}
{panels}
)
The problem:
On first message from the WS the third element is added to the array properly (example-1)
, fourth one is added properly but it also overwrites the third element (example-2)
making them exactly the same. After fourth it gets strange, where some are overwritten and some are not.
I've tried:
Moving the state updating out of useEffect or removing useEffect completly
Storing current_array in a temp var before any logic
Adding a counter to track which tab's id is the latest -> tracking state of just one number works
const [tab_count, setTabCount] = useState( INITIAL_ARRAY.lenght );
Using counter to try to force rendering
Setting up a fixed number of objects in the initial array and just update which ever is needed (with and without counter)
Updating based on the previous value
setNewArray( prevArray => {
logic
return [...prevArray, new_tab];
}
After the first WS message, if the code is changed/saved and webpack compiled, the next message will add a new element to the array properly.
EDIT - Solved:
Managed to solve this by building a new object (instead of using the template) before adding it to the array.
I have a sumButtonsDict state variable that stores a dictionary of objects. I've also built a simple addSumButton() function add a new object into the sumButtonsDict:
const [sumButtonsDict, setSumButtonsDict] = useState({})
function addSumButton(sum) {
const dictKey = Object.keys(sumButtonsDict).length
const sumButtonDict = {
sum: sum,
isActive: false
}
setSumButtonsDict(prevState => ({...prevState, [dictKey]: sumButtonDict}))
}
As you can see, the function stores every new dictionary item at a key corresponding to the index it's on (e.g., first item has key 0, second item has key 1...), but the correct key is derived from the count of objects already existing in sumButtonsDict.
When the component mounts, I add 5 new buttons using the following:
useEffect(() => {
addSumButton(10)
addSumButton(25)
addSumButton(50)
addSumButton(100)
addSumButton(250)
}, [])
but only 1 ends up existing in sumButtonsDict. I suspect this is because setState() doesn't update the state variable immediately, and hence when I call Object.keys(sumButtonsDict).length it keeps on returning 0 even though the addSumButton() has run multiple times before.
How can I get around this?
You're already using the function version of setSumButtonsDict to get the previous state, which is the right thing to do. You just need to move the other bits of code into the function too, so the entire calculation uses prevState:
function addSumButton(sum) {
setSumButtonsDict(prevState => {
const dictKey = Object.keys(prevState).length;
const sumButtonDict = {
sum: sum,
active: false,
}
return {...prevState, [dictKey]: sumButtonDict}
});
}
I'm making a React-Native application. Thanks to everyone's help I could somehow make that work except for toggling YES and NO. Once the user clicks on a button I just want to check if that clicked item data already exists in the state, if so I want to update it. If it does not exist then it should be added to the state as a new Item.
I already coded the above logic, my code is working, but not returning correct output, elements are not adding or updating to the state array properly. How do I fix this code?
I want to generate output like this
[{
chelistid:231,
validiary : "YES",
remark : "Hello"
},{
chelistid:232,
validiary : "NO",
remark : "asddddd"
}]
My code
const [reqData, setReqData] = useState([]);
//Modify yes clicked
const yesClicked = (element) => {
let req = {
"chelistid": element.chelistid,
"validiary": "Yes",
"remark": element.remark
}
createCheckList(req);
}
//Modify no clicked
const noClicked = (element) => {
let req = {
"chelistid": element.chelistid,
"validiary": "No",
"remark": element.remark
}
createCheckList(req);
}
const createCheckList = (data) => {
const index = reqData.findIndex(x => x.chelistid === data.chelistid)
var modifiedArray = reqData
if (index !== -1) {
//Remove the element from the array
modifiedArray.splice(index, 1);
}
setReqData([modifiedArray, data]);
}
The problem is it seems like you are not spreading the array to append the data element. What you are doing by [modifiedArray, data] you are creating an array that contains an array and data something like [[modified array content here], data]. But actually, you want to append to modified array instead. For that, you need to expand the modified array by using ... which is called spread syntax. (Learn here) So, your code would look like:
setReqData([...modifiedArray, data]);
createNewList = (id, input) => {
const foundCard = {...this.state.cards.find(card => id === card.id)};
this.setState(foundCard.list = [...foundCard.list, input]);
};
Hello everyone
There is an array of data (list), which is stored in the state for each object (card).
Problem: I can’t seem to add a new element to this array.
The way it is set up creates only one element and change it every time. But I need to create a new one every time. I tried to create a separate array, add to it using newArray.push (input) and then do this.setState (foundCard.list = [... foundCard.list, ... newArray])`, but have the same result.
I don’t use Redux, because I just started to learn React and I store everything in state yet.
Thanks in advance for your reply.
createNewList = (id, input) => {
const newCards = this.state.cards.map(card => {
if (card.id === id) card.list = [...card.list, input];
return card;
});
this.setState({
cards: newCards
});
};