When I remove an array item from my state array, I'm also updating the prices after removing the array item. But prices are not updating. I have tried every thing, but didn't get any solution.
export default function CustomizerState(props) {
const initialTextItem = {
text: "Hello",
neonPrice: 0,
backplatePrice: 0,
neonPower: 0,
totalPrice: 0
}
const [settings, setSettings] = useState({
textItems: [initialTextItem],
libraryItems: [],
accessories: [...],
finalPrice: null
})
const removeItem = (id, itemType = "textItems") => {
const filteredItems = settings[itemType].filter((item) => {
return item.id !== id
})
setSettings((prevState) => (
{...prevState, [itemType]: filteredItems}
))
finalPrice()
}
const finalPrice = () => {
const textItemsPrice = getTotalPrice()
const libraryItemsPrice = getTotalPrice("libraryItems")
const accessoriesPrice = getTotalPrice("accessories", "unitPrice")
console.log(textItemsPrice, libraryItemsPrice, accessoriesPrice)
const finalPrice = textItemsPrice + libraryItemsPrice + parseInt(accessoriesPrice)
setSettings((prevState) => (
{...prevState, finalPrice}
))
}
const getTotalPrice = (itemType = "textItems", priceKey = "totalPrice") => {
let price = 0
settings[itemType].map((item) => (
price = price + (item[priceKey] * item.quantity)
))
return price
}
return (
<CustomizerContext.Provider value={{settings, addTextItem,
removeItem}}>
{props.children}
</CustomizerContext.Provider>
)
}
For now, it is behaving like when I remove any item, it doesn't update the finalPrice object item, but when I remove another item then it updates the prices for previous items. I don't know why it is behaving like this.
Can someone please have a look on my code and tell me what is wrong with it and how can I fix this?
You're calling finalPrice()right after you set your state
that triggers a re-rendering. You have to trigger the change using useEffect hook like this:
useEffect(() => {
finalPrice()
}, [settings]
You should probably consider separating your price from the rest of your settings.
Instead of calling a function right after updating the list, do the calculations before and update the state altogether. The problem with your approach is that when the calculus is being made, the state haven't updated yet, so when the function finalPrice() runs it takes the previous value.
I sincerely recommend you to use a Reducer instead, a single state with so many parameters may be troublesome.
Refer to useReducer, it will make your life easier.
Related
Now I know the title may be a bit vague so let me help you by explaining my current situation:
I have an array worth of 100 object, which in turn contain a number between 0 and 1. I want to loop through the array and calculate the total amount e.g (1 + 1 = 2).
Currently using .map to go through every object and calaculate the total. When I am counting up using the useState hook, it kinda works. My other approach was using a Let variabele and counting up like this. Although this is way to heavy for the browser.
I want to render the number in between the counts.
const[acousticness, setAcousticness] = useState(0);
let ids = [];
ids.length == 0 && tracks.items.map((track) => {
ids.push(track.track.id);
});
getAudioFeatures(ids).then((results) => {
results.map((item) => {
setAcousticness(acousticness + item.acousticness)
})
})
return (
<div>
Mood variabele: {acousticness}
</div>
)
What is the proper way on doing this?
I think this is roughly what you are after:
import {useMemo, useEffect, useState} from 'react';
const MiscComponent = ({ tracks }) => {
// Create state variable / setter to store acousticness
const [acousticness, setAcousticness] = useState(0);
// Get list of ids from tracks, use `useMemo` so that it does not recalculate the
// set of ids on every render and instead only updates when `tracks` reference
// changes.
const ids = useMemo(() => {
// map to list of ids or empty array if `tracks` or `tracks.items` is undefined
// or null.
return tracks?.items?.map(x => x.track.id) ?? [];
}, [tracks]);
// load audio features within a `useEffect` to ensure data is only retrieved when
// the reference of `ids` is changed (and not on every render).
useEffect(() => {
// create function to use async/await instead of promise syntax (preference)
const loadData = async () => {
// get data from async function (api call, etc).
const result = await getAudioFeatures(ids);
// calculate sum of acousticness and assign to state variable.
setAcousticness(result?.reduce((a, b) => a + (b?.acousticness ?? 0), 0) ?? 0)
};
// run async function.
loadData();
}, [ids, setAcousticness])
// render view.
return (
<div>
Mood variabele: {acousticness}
</div>
)
}
I am a beginner in React, I have a question regarding rendering, which consists of I use useEfect to render the change in a variable, which is decorated in a useState
Here are the statements:
const CardRepo: React.FC<DadosCardRepo> = ({ Nome }) => {
const [nomeRepo, setNomeRepo] = useState(Nome);
const [data, setData] = useState({});
const [languages, setLanguages] = useState<Languages[]>([]);
This is a component, which receives an object with a Name, and sets this value Name in nameRepo
Here are the two methods:
useEffect(() => {
getRepo(nomeRepo)
.then(data => {
setData(data);
});
}, [nomeRepo]);
useEffect(() => {
getLanguages(data['languages_url'])
.then(data => {
let total = 0;
const languagesRef: Languages[] = [];
Object.keys(data).map((key) => {
total += data[key];
languagesRef.push({ Nome: key, Porct: data[key] });
});
languagesRef.map((language, i, array) => {
setLanguages((oldValues) => [...oldValues, { Nome: language.Nome.toLowerCase(), Porct: +((language.Porct * 100) / total).toFixed(2) }]);
});
});
}, [data]);
Where the first useEfect I run when making a change to nomeRepo, and in this method, I make a request to the getRepo service
And the second one, when I change the date, and in this method, I make a request to the getLanguages service, and do the processing of the response to make a list with it, where I assign languages.
Here is the listing:
{
languages.map((el, i) => {
return (
<div className="progress-bar" id={el.Nome} key={i} style={{ width: el.Porct + '%' }}></div>
);
})
}
My question is related to rendering, I know that I will only be able to list something, if I use useEfect in the variable "languages", where I will be assigning values and with the changes in the variable to render again, otherwise I couldn't.
But when I make any changes to the code, it renders only where it was changed, but it assigns the same values that were assigned, for example: [1,2,3] => [1,2,3,1,2,3].
I tried to put setLanguages ([]), but it has the same behavior
I was wondering how to fix this and if it's a good practice to make calls and list that way.
first, there is one bad practice where you create a derived state from props. nomeRepo comes from props Nome, you should avoid this derived states the most of you can. You better remove nomeRepo state and use only Nome.
your first use Effect becomes:
useEffect(() => {
getRepo(Nome)
.then(data => {
setData(data);
});
}, [Nome]);
you second useEffect has a setState triggered inside a map. That will trigger your setState multiple times, creating several rerenders. It's best to prepare your array of languages first, then call setState once.
Also, you should use forEach instead of map if you are not consuming the returned array, and only running some script on each element.
One thing to notice, is you are adding new languages to old ones. I assume that languages should be related data['languages_url'] value, this way you wouldn't include the old languages to the setLanguages.
One thing you could consider is to keep your total values instead of percentages. you can store total into a variable with useMemo, that will depend on languages. And declare a your percentage function into a utils folder and import it as need it.
refactored second useEffect:
// calculate total value that gets memoized
const totalLangCount = useMemo(() => languages.reduce((total, val) => total + val.count , 0), [languages])
useEffect(() => {
getLanguages(data['languages_url'])
.then(data => {
const languagesRef: Languages[] = [];
Object.keys(data).forEach((key) => {
// prepeare your languages properties
const nome = key.toLowerCase();
const count = data[key];
languagesRef.push({ nome, count });
});
// avoid multiple setLanguages calls
setLanguages(languagesRef)
});
}, [data]);
languages rendered:
{
languages.map((el, i) => {
return (
// here we are using array's index as key, but it's best to use some unique key overall instead
<div className="progress-bar" id={el.nome} key={i} style={{ width: percentage(el.count, total) }}></div>
);
})
}
percentage function reusable:
// utils percentage exported
export const percentage = (count, total) => ((count * 100) / total).toFixed(2) + '%'
recently I started to work in a proyect and I notice that I need some way to modify a value without losing my previous state. Basically work with an array. However I don't know how to do that.
Can anyone help me by telling me how can I modify an state (I'm using react hooks by the way...) to add more values to the previous state in an array?
Sorry not posting code or something like that, I don't even know how to write that.
Thank you in advance.
Use ES6 spread operator to push new object to the existing array.
Adding a new object to an Array:
const [todo, setTodo] = useState([{ task: "Todo 1" }]);
const addTodo = () => {
let newTodoTask = { task: `Task ${todo.length + 1}` };
setTodo(tasks => [...tasks, { ...newTodoTask }]);
};
Modifying an object in an Array:
const editTask = (e, taskId = 0) => {
setTodo(tasks =>
tasks.map((task, idx) =>
idx === taskId ? { task: "Edited Todo 1" } : { ...task }
)
);
};
Deleting an object from an array
const deleteTask = (e, taskId = 0) => {
setTodo(tasks => tasks.filter((task, idx) => idx !== taskId));
};
Find the simple working example here.
https://codesandbox.io/s/unruffled-elgamal-h7fhg?file=/src/App.js:482-593
First, Learn basic ES6 before start working on the project.
You can use the previous value returned from the setState to update the existing Array
import { useState } from 'react';
export default function Test() {
const [state, setstate] = useState([1,2,3,4);
return <div onClick={() => setstate((prev) => [...prev,99])}>Heyy{state}</div>;
}
I am trying to use React Hooks but somehow my state is not updating. When I click on the checkbox (see in the example), I want the index of the latter to be added to the array selectedItems, and vice versa
My function looks like this:
const [selectedItems, setSelectedItems] = useState([]);
const handleSelectMultiple = index => {
if (selectedItems.includes(index)) {
setSelectedItems(selectedItems.filter(id => id !== index));
} else {
setSelectedItems(selectedItems => [...selectedItems, index]);
}
console.log("selectedItems", selectedItems, "index", index);
};
You can find the console.log result
here
An empty array in the result, can someone explain to me where I missed something ?
Because useState is asynchronous - you wont see an immediate update after calling it.
Try adding a useEffect which uses a dependency array to check when values have been updated.
useEffect(() => {
console.log(selectedItems);
}, [selectedItems])
Actually there isn't a problem with your code. It's just that when you log selectedItems the state isn't updated yet.
If you need selectedItems exactly after you update the state in your function you can do as follow:
const handleSelectMultiple = index => {
let newSelectedItems;
if (selectedItems.includes(index)) {
newSelectedItems = selectedItems.filter(id => id !== index);
} else {
newSelectedItems = [...selectedItems, index];
}
setSelectedItems(newSelectedItems);
console.log("selectedItems", newSelectedItems, "index", index);
};
I am working with React and I can't seem to define properly the state of a component (handlePoints). When I want to assign a new state using shallow copy it creates a new entry (key).
Apologies that part of the question is not in code. I couldn't edit it here. If you need more information please reach out. Thanks! I've added an image where you can see that there is a new entry added instead of a modification happening to an existing entry.
cons anecdotes = ['Anecdote A', 'Anecdote B', 'Anecdote C', 'Anecdote D', 'Anecdote E', 'Anecdote F']
const Button = ({text, onClick}) => <div>
<button onClick={onClick}>{text}</button>
const records = () => {
const temp = {};
for (let i = 0; i < anecdotes.length; i++) {
temp[i] = 0;
}
return temp}
const copy = {...records()};
const App = (props) => {
console.log('Copy', copy);
const [selected, setSelected] = useState(0);
const [points, setPoints] = useState(copy);
const handleSelection = () => {
setSelected((Math.random() * (anecdotes.length) ) << 0);
};
const handlePoints = () => {
setPoints({...copy, selected: copy[selected] += 1})
};
return (
<div>
<p>{JSON.stringify({copy})}</p>
<p>{JSON.stringify({points})}</p>
<p>Selected: {selected}</p>
<Button onClick={handleSelection} text={'next anecdote'}/>
<Button onClick={handlePoints} text={'vote'}/>
has {copy[selected]} votes <br/>
{props.anecdotes[selected]} <br/>
</div>
);};
The line where I cannot set the state correctly is:
const handlePoints = () => {
setPoints({...copy, selected: copy[selected] += 1})
};
As you can see, a new entry was added instead of updating one of the existing one.
In terms of why a new field with key 'selected' is being added, is due to how you are creating the new object.
If you want to to update the key with the variable selected, you would need to modify the handlePoints function to be:
const handlePoints = () => {
setPoints({...copy, [selected]: copy[selected] += 1})
};
The brackets around the variable selected is how it determines to use the value of the variable selected. Instead of interpreting it as a the key 'selected'. This is making use of the computed property names introduced in emca2015. More info - Computed property names
Hopefully this solves your problem :)
Good luck :D
Also:
As for what I can see, your copy function it isn't actually copying any object, it seems to be a default? Or is the purpose just to create the keys for the number of anecdotes there are?
Just curious XD