I am trying to build switches that create an array and have this so far:
const [playgroundFilters, setPlaygroundFilters] = useState([initialF]);
const updateItem = (whichvalue, newvalue) => {
let g = playgroundFilters[0];
g[whichvalue] = newvalue;
setPlaygroundFilters(g, ...playgroundFilters.slice());
console.log(playgroundFilters);
};
When I call up updateItem onPress it works once and every subsequent time I get an error "undefined is not an object evaluating g"
Is there an easy way to fix this?
setPlaygroundFilters expects an array so you would need to call it like that
setPlaygroundFilters([g, ...playgroundFilters.slice()]);
instead of
setPlaygroundFilters(g, ...playgroundFilters.slice());
I'm not sure you actually wants to use .slice() like that here, since it just returns the same (cloned) array.
Related
I'm trying to create a resuable component that will be able to take a function passed down into it, allowing it to update the state of the parent component. I've got a demo on CodeSandbox that shows the issue.
TrackListing is passed the removeTrack function from the parent, and when the delete button is clicked inside the TrackListing, the function is called with the rows index to be removed from the tracks state. Unfortunately this doesn't seem to update the tracks state.
Your not changing the state, tracks it's the same tracks.
In JS if you do -> var a = [1,23]; var b = a; console.log(a === b); it would return true. Assigning an array to another var, does not make a new array.. Doing var a = [1,23]; var b = [...a]; console.log(a === b); will do a shallow copy of a, and this as expected will return false. IOW: In react causing a state change.
Also splice(index,index), I'm pretty sure you meant splice(index, 1).
const removeTrack = (index: number) => {
const t = tracks;
t.splice(index, index);
console.log(t);
setTracks(t); //t is still the same instance of t.
};
So you will need to make a copy of t.
const removeTrack = (index: number) => {
const t = tracks;
t.splice(index, 1);
setTracks([...t]); //lets make a copy of `t` state has changed.
};
As pointed out in the comments, using the callback version of setState is a good idea, it will prevent issues of scope, here scope isn't an issue, but it's easy to get caught out by it.
Also another issue I've found with your current implementation you might want to look into, using array index as a key is not a good idea, it's not unique and you could get some funky rendering quirks, you really need unique ids for your tracks to use as the key
eg. This bit ->
tracks.map((track, index) => (
<tr key={index}> //not a good idea.
tracks.map((track, index) => (
<tr key={track.id}> //much better
Also when it comes to deleting, use the track id, using the callback/filter version in comments from Ali, you could also then just change removeTrack(track:Track) {}, and pass the track. IOW: remove index array positions from all code.
I've done a fork of your CodeSandBox with these changes to make it easier. CodeSandBox
I wrote a function calcNewtonPath with 4 parameters as below. However when i call it, the function changes the value of parameter matA unexpectedly and i can't understand why.
function calcNewtonPath(matA,matB,m,n){
let temp = matA.slice() //i tried this to prevent the unexpected change but it didn't work
for(let i=0;i<m;i++){
temp[i].push(-matB[i])
}
return matrix.solve(temp,m,n)
}
The function solve also changes the value of temp too! But it's a bit complicated so i think i will not put the code here.
Can anyone help me about this?
UPDATED:
.slice() will just do a shallow copy.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
That means for example:
const a = [[1,2,3]]
const b = slice.a();
b[0][1] = 4;
a array will also be changed.
a will be [[1,4,3]] as well as b;
One easy solution would be to make a deep copy of an array:
const deepCopy = JSON.parse(JSON.stringify(array));
Am using function based component and am having trouble in pushing a subarray into a useState array.
my code is shown below. There is an array called mode coming from the props which i need to append it as a sub array of more
const ViewCharts = (props) =>{
//other codes
let [more,setMore] = useState([])
useEffect(()=>{
console.log(props.mode,' mode array')
let temp = [...more,props.mode]
console.log(temp, ': this will append to more')
setMore(temp)
setTimeout(()=>{
console.log(more,'after setting')
},2000)
},[//some value])
}
when the props.mode has value ['top','bottom'] i expect more to have value of [['top','bottom']] and when next time if the props.mode is ['top'] i need more to have [['top','bottom'],['top']]. but this is what am getting when i run the above code.
["top"] mode array
["top"] : this will append to more"
[] : "after setting"
why the setMore is not adding array even when the temp is having the expected value.
If I remember correctly the useState variable will change value in the next render when you set it. You are trying to read the new more value in the same render you've changed it (as you are reading it in the same effect you've set the value in), which will be [] the first time as that's how you initialised it.
Try creating a second useEffect with a more dependency to see if it gives you the value you want:
// You can also just console.log() before the return, needing no `useEffect` to see if the value indeed changed.
React.useEffect(() => {
console.log('More: ', more);
}, [more]);
https://reactjs.org/docs/hooks-state.html#recap
Line 9: When the user clicks, we call setCount with a new value. React will then re-render the Example component, passing the new count value to it.
I would suggest reading the hooks API to better understand how they work.
I have a problem passing an array.push function in javascript having a code like this:
const array = [];
addToArray("works", (e) => array.push(e));
addToArray("notWorks", array.push);
doConsoleLog("hello", console.log);
function addToArray(element, pushFn) {
pushFn(element);
}
function doConsoleLog(message, log) {
log(message);
}
Just curious how's that first call of addToArray works, but second causes TypeError: Cannot convert undefined or null to object. How is it possible? Type of array.push is a function. It is not possible to pass functions directly like in the case of console.log above, which works pretty well?
push is a method of array. When invoked, it uses this as a reference to the object it is being called on. If you pass it around like a normal function, you will lose that reference, the method will use the value of this defined at the moment of the call (inside addToArray, this is probably document.window), and will therefore fail.
In order to pass around a method, you need to bind it to the object you want it to operate on, in this case array. This is true for any object method in JavaScript. Function.prototype.bind() exists exactly for this purpose: it "binds" a function (in this case your method) to a given value of this, in order to work correctly.
const array = [];
addToArray("now it works", array.push.bind(array));
// or
addToArray("now it works", Array.prototype.push.bind(array));
function addToArray(element, pushFn) {
pushFn(element);
}
Once you pass a function like you did in the second case, it executes itself immediately, and array.push doesn't really mean something.
A callback function should be executed later on in the outer function.
Unlike the second example, in the first one you don't execute the function immediately, but only when you call it inside the outer function.
Your code have a lot of mistakes. What exactly should do this code? if you want to push new items in array use spread operator
const array = [...[], "works", "notWorks"]
const array = [];
addToArray("works", (e) => array.push(e));
addToArray("notWorks", array.push);
doConsoleLog("hello", console.log);
function addToArray(element, pushFn) {
pushFn(element); //function must return something
}
function doConsoleLog(message, log) {
log(message); //function must return something
}
I want to implement observable of array that auto-refresh itself. I do it like this:
const list$ = Observable.create(function(observer) {
getList(list => observer.next(threads);
}));
const liveList$ = Observable.interval(2000).switchMapTo(list$);
When I do subscribe to the liveList$ stream I get values only after n ms. as expected.
The question is how can I get values after first call of getList immediately and each next call with interval?
P.S. I've tried to $list.switchMapTo($liveList).subscribe(console.log) but nothing is changed in behaviour of the chain
Use the timer operator instead. It can be passed an initial delay, as well as a period:
Observable.timer(0, 2000).switchMapTo(list$);