In the example here. I have to sliders and I drag them. Because I have two values I store them in useState as an array.
Then storing as an array the ComponentsWithArrayAsProp1 doesn't see changes in the state as it is the same array and does not re-render itself.
In the second example, I store values as values.toString(), but this is not a good solution.
What is a good solution for this case?
Then storing as an array the ComponentsWithArrayAsProp1 doesn't see changes in the state as it is the same array and does not re-render itself.
you are right we can solve it by creating new array everytime as follows,
function onChange1(values) {
setValues1([...values]);
}
but i think there is something wrong with react-slider .because your approach of setting array ,setValues1(values) works when we click on different points of slider1. but it deosn't work when we drag it. there is something else going wrong here ?
Option 1:
As you said, the ComponentsWithArrayAsProp1 doesn't see the changes because the array reference is the same, so it doesn't rerender. But you can save a copy of the array instead of the array itself.
function onChange1(values) {
setValues1(values.slice());
// Another way to do this
// setValues1([...values]);
}
Option 2:
You can also save the array numbers individually, like so:
const [values1Start, setValues1Start] = useState(0);
const [values1End, setValues1End] = useState(0);
function onChange1(values) {
setValues1Start(values[0]);
setValues1End(values[1]);
}
return (
<ComponentsWithArrayAsProp1 values={[values1Start, values1End]} />
);
Related
I have an array in a react state hook. I'm pushing values to it in a way that i assumed was the correct way, yet the array is always empty when i try to log it. What about my method of pushing to the array is not correct?
const [betHistory, setBetHistory] = useState([""]);
Inside my function (which i've tested is being called, and betCount is never blank)
function placeBet() {
console.log(betCount);
setBetHistory(betHistory => [...betHistory, betCount.toString()]);
console.log(betHistory);
}
The log is always blank.
Setting the state will update the value on the next render, not immediately. If you need to access the updated value right away, store it in a variable:
function placeBet() {
const newBetHistory = [...betHistory, betCount.toString()];
setBetHistory(newBetHistory);
console.log(newBetHistory);
}
I have a little question. Let's assume this code
const [ChatDetailsRender, setChatDetailsRender] = useState([]);
//ChatDetailsRender it s equal= {10,20}
array=[1,2,3,4]
array.forEach((nr)=>{
setChatDetailsRender();
//here some code to add the array for each value in useState})
This is some simple example about what i wanna do and i have some difficulties because i also have some database calls. Everything looks fine but how can i update ChatDetails useState without overwriting data? like just merge these 2 arrays. Sorry, i think this questions was already here but all i can find it's examples with objects in useState, and my example contains just an simple array. Thanks.
You can concat the new value to the existing value while updating and use callback approach to set state.
const [ChatDetailsRender, setChatDetailsRender] = useState([]);
//ChatDetailsRender it s equal= {10,20}
array=[1,2,3,4]
setChatDetailsRender(previousChatDetails => previousChatDetails.concat(array));
I wrote a custom form component called SingleFilterContainer
Within the parent component called FiltersContainer, I initialize a state filters which has an array of a single filter initially and respective setFilters function to modify the array of filters. And also has a button to add a filter. And in the render function I use filters.map to render SingleFilterContainer component multiple times. So far this works. But I want to add a delete filter button. I put this inside the SingleFilterContainer. When I click this it should update the state filters in the parent component and delete the ith filter and render the rest of the filters normally. Or render the whole map of filters again.
I'm new to react and hooks, and I feel like I'm missing something. I'm at it for the past three days and I'm a little bit lost.
Here is the sandbox https://codesandbox.io/s/editing-filters-j2qrp
I feel like how I'm handling state is completely wrong. And maybe I should use redux? But I want the SingleFilterContainer to be like an ephemeral form. Or should the delete filter button be within the parent component? and repeat it using map?
TL;DR Fixed fork
The problem in your code is that you call the function on render onClick={handleDeleteFilter(i)} the onClick expects a reference to a function, but if you want this code to work, then the method that you are passing from the parent to the child needs to return a function also.
Then your handleDeleteFilter will look like this:
function handleDeleteFilter(i) {
return function() {
filters.splice(i, i + 1);
setFilters(filters => filters);
}
}
Also in your case you don't need to pass i + 1 into splice as the second argument, as the second argument is the amount of items to remove. Which in your case is just 1. Docs
You pass the i to the first function and the second one will see it due to closure.
And then to the removing of an element.
Mutating the state is bad practice, so you can have a local variable with a copy of the state, which you can manipulate and then update the state. That way you can use whatever you want, but on the local variable.
So your updated handleDeleteFilter would look like
function handleDeleteFilter(i) {
return function() {
const clone = [...filters]
clone.splice(i, 1);
setFilters(clone);
}
}
or with .filter
function handleDeleteFilter(i) {
return function() {
const clone = filters.filter((item, index) => index !== i)
setFilters(clone);
}
}
this way you don't need a new variable as .filter returns a new array. Docs
Here's my fork.
The following is my changes.
function handleDeleteFilter(i) {
setFilters(filters => filters.filter((e, index) => index !== i));
}
Important notes:
Ideally, you need to assign an id property for each item in the array instead of comparing by index.
Don't directly mutate the state. .splice directly mutates the state. Learn to use .map, .filter & .reduce array functions
I have created the following demo to help me describe my question: https://codesandbox.io/s/dazzling-https-6ztj2
I have a form where I submit information and store it in a database. On another page, I retrieve this data, and set the checked property for the checkbox accordingly. This part works, in the demo this is represented by the dataFromAPI variable.
Now, the problem is that when I'd like to update the checkboxes, I get all sorts of errors and I don't know how to solve this. The ultimate goal is that I modify the form (either uncheck a checked box or vice versa) and send it off to the database - essentially this is an UPDATE operation, but again that's not visible in the demo.
Any suggestions?
Also note that I have simplified the demo, in the real app I'm working on I have multiple form elements and multiple values in the state.
I recommend you to work with an array of all the id's or whatever you want it to be your list's keys and "map" on the array like here https://reactjs.org/docs/lists-and-keys.html.
It also helps you to control each checkbox element as an item.
Neither your add or delete will work as it is.
Array.push returns the length of the new array, not the new array.
Array.splice returns a new array of the deleted items. And it mutates the original which you shouldn't do. We'll use filter instead.
Change your state setter to this:
// Since we are using the updater form of setState now, we need to persist the event.
e.persist();
setQuestion(prev => ({
...prev,
[e.target.name]: prev.topics.includes(e.target.value)
// Return false to remove the part of the array we don't want anymore
? prev.topics.filter((value) => value != e.target.value)
// Create a new array instead of mutating state
: [...prev.topics, e.target.value]
}));
As regard your example in the codesandbox you can get the expected result using the following snippet
//the idea here is if it exists then remove it otherwise add it to the array.
const handleChange = e => {
let x = data.topics.includes(e.target.value) ? data.topics.filter(item => item !== e.target.value): [...data.topics, e.target.value]
setQuestion({topics:x})
};
So you can get the idea and implement it in your actual application.
I noticed the problem with your code was that you changed the nature of question stored in state which makes it difficult to get the attribute topics when next react re-renders Also you were directly mutating the state. its best to alway use functional array manipulating methods are free from side effects like map, filter and reduce where possible.
Sorry about the confusing title, I really didn't know what the categorize this as. I was having an issue regarding changing a variable that was set to an Array element. I have since figured out a way to accomplish what I wanted, but this was still bugging me so I figured I'd ask here.
playCard(event){
let tempArray = this.state.playerDeck;
let playArray = this.state.playerDefaultCards;
playArray.push(tempArray[event.target.id]);
console.log(playArray[1].image); //logs image data
let tempObject = tempArray[event.target.id];
tempObject.image = "Test";
console.log(playArray[1].image); //logs "Test"
tempArray[event.target.id] = tempObject;
this.setState ({playerDeck: tempArray, playerDefaultCards: playArray});
console.log(this.state.playerDefaultCards[1].image); //Also logs "Test"
}
Basically I have 2 arrays of objects with image sources. When an image is clicked on, I wanted it to go from the playerDeck to the playerDefaultCards, and then I just wanted to change the image source in the playerDeck to a blank string.
I thought that if I made a temporary variable and set it equal to the array element, changed that temporary variables image property and then set the array element equal to that new object everything would work out.
However when I change tempObject.image = "Test" it changes in both the tempArray and the playArray, which to my knowledge shouldn't happen. This is the source of my confusion.
Obviously I'm missing something but so far I haven't been able to figure it out.
playArray.push(tempArray[event.target.id]) copies the reference at tempArray[event.target.id] to the object rather than the object itself.
You can use Object.assign to create a new object for the card you want to move, then you can edit the original.
You should also .slice() the state arrays to make sure you do not change them outside of setState.
Something like this:
playCard(event){
const tempArray = this.state.playerDeck.slice();
const playArray = this.state.playerDefaultCards.slice();
const selectedCard = Object.assign({}, tempArray[event.target.id]);
playArray.push(selectedCard);
tempArray[event.target.id].image = "Test";
this.setState({playerDeck: tempArray, playerDefaultCards: playArray});
}
One thing to watch out for is setState is fast, but it is not instant. A console.log called right after this.setState will possibly execute before the update is completed and return the old value. this.setState can take a callback function if you need to do something as soon as it finishes.
Object.assign is not supported by all browsers, so you may need a polyfill for it.
Object.assign documentation