setTimeout in React after render - reactjs

This is my first question. Anyway, I am new to React, I started 2 weeks ago.
I am making a flipbook where updateAll updates all allPages in the book.
The problem is that when the page is flipped, the z-index needs to be changed in order to adhere to the correct stacking.
When flipping right, we see the z-index of the page on the right change too quickly, and the flipping isn't smooth. Go check it out yourself to see what I mean.
https://cookbookreact.netlify.app/
I need to use setTimeout to set the z-index 200 milliseconds after the page is flipped, so that the page flipping is smooth. But how? I know I need to use useEffect() I think. Right?
Whenever I try to use useEffect(), it says I am breaking the rules of hooks, so I don't even know what to do.
The last z-index needs to be updated so that we can scroll on that page by the way.
// we update any pages that were flipped
updateAll((prevAllPages) => {
for (let i = 0; i < amount; ++i) {
// flip current page
prevAllPages[pageNum + i].isFlipped =
!prevAllPages[pageNum + i].isFlipped;
// zIndex
prevAllPages[pageNum + i].zIndex =
999 + prevAllPages[pageNum + i].pageNum;
}
return prevAllPages;
});
/*** Set timeout goes here ***/
setTimeout(function () {
// so that last page can be seen
updateAll((prevAllPages) => {
if (pageNum < maxPages / 2) {
prevAllPages[pageNum + amount].zIndex =
999 + prevAllPages[pageNum + amount].pageNum;
}
return prevAllPages;
});
// then we update the page number
updatePageNum((prevPageNum) => {
return prevPageNum + amount;
});
}, 200);
I'd appreciate any help, Thank you
David

When updating state try creating a new variable to return instead of changing the prevAllPages and returning it again.

Related

Removing a hyphen to the last number of a counter app but also not repeatedly duplicating the same value

The problem
The counter app that I build can save each number in each entry. And each number is separated using the hyphen(-), but the problem is how can I remove the hyphen in the last number or entry but also not repeatedly duplicate the same number whenever I hit the save button twice.
What I Try
Although, I managed to get the trick by asking for a solution on chatGPT. But, it turns out it duplicates the same number whenever I clicked the save button.
Here's the code:
const save = () => {
// count !== 0 && setCounter(counter + count + "-"); previous code that adds the hyphen
if (count !== 0) {
const updatedCounter = counter.endsWith('-')
? counter + count
: counter + count + "-";
setCounter(updatedCounter);
}
};
I think the duplication of the same value occurs on the else statement of : counter + count + "-";
Because I tried to re-arrange the variables and the hyphen by this : counter + "-" + count; and it didn't duplicate the same number twice after clicking the save button. But, the problem is it's not the intended output. Where the intended output would be 1-1-2-3-4 and not 11-2-3-4 or -1-1-2-3-4
Also, whenever I clicked the reset button, it returns an error of Uncaught TypeError: counter.endsWith is not a function
const reset = () => {
setCount(0);
setCounter(0);
setAddAll([]);
setAddOnce(false);
};
You can also view the full project in stackblitz
This is just a simple project with some additional features that I implement to practice my coding skills in React JS. So, comments and suggestions are much appreciated. Thanks!
In your reset function you're setting the counter to a number instead of a string.
Instead use this, this way you'll reset back to your initial value.
setCounter("");
Full reset function
const reset = () => {
setCount(0);
setCounter("");
setAddAll([]);
setAddOnce(false);
};

Problem with this react flow, maximum update depth exceeded

I have the following function
compareProducts = (empresa) => {
console.log(empresa.listaProductos)
let headerSetIn = false;
for (let i in empresa.listaProductos) {
//case1 : lookup for some data in an array, if found, setState and exit the whole function
if (pFichaInternacional && pFichaInternacional.length > 0) {
console.log("caso1")
let product: any = pFichaInternacional;
let nombreEmpresaApi = empresa.listaProductos[i].nombre
let productoFiltrado = product.filter(i => i.referencia == nombreEmpresaApi)
productoFiltrado = productoFiltrado[0]
if (productoFiltrado) {
headerSetIn = true
this.setState({
headerCardText: productoFiltrado.descripcion.toString(),
headerButtonText: productoFiltrado.label.toString(),
})
break;
}
}
}
//case 2: case1 didnt found the data, so we setup some predefined data.
if (!headerSetIn && pFichaInternacional.length > 0) {
let product: any = pFichaInternacional;
this.setState({
headerCardText: product[0].descripcion.toString(),
headerButtonText: product[0].label.toString()
})
}
}
Im receiving a
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
I have also tried using a setstate , instead of a local variable to set the headerSetIn parameter. But if I do it, I think js doesnt have time to evaluate the change, and both are executed, instead of only 1
Ive tried to use () , => after the first state, but it doesnt make sense in my flow
As far as I can understand is that you are calling setState in a for loop, which is not a good thing. Every time a certain condition is met setState is called, so you are constantly setting the state and rerendering smashing your performance and causing this.
I would suggest using a variable and get the needed data in the for loop then after you have excited the for loop simply use setState to set the state after all the data has been looped trough.

How can I make React append a number to a variable? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am trying to automate fetching data from a source and assigning it to a variable that goes up by 1 each loop. Each call to the data source returns a different URL (of a picture of a cat, in this case).
var i;
var urlCount = 7;
for (i=0; i < urlCount; i++) {
this.setState({...this.state, isFetching: true});
var response = await axios.get(USER_SERVICE_URL);
this.image = response.data[0];
url[i] = this.image.url;
console.log([i] + url[i]);
}
What I'm wanting it to do is create a list similar to the following:
url0 = (url here),
url1 = (another url here),
url2 = (yet another url here),
etc...
until the loop meets its condition. It works when I set the code to a manual value, like this:
url0 = this.image.url;
console.log([i] + url0);
But it doesn't work when I try to replace 0 with [i], as in the first example. It seems to be treating [i] as a property of url, instead of appending it to url. How can I make React append a number to a variable so that I can achieve the desired result above?
would not recommend to do this with a for loop, also missing information about the component itself. Also using an object here as an example, but would recommend an array.
// assuming a functional component
// add a state with a object (would rather go with an array)
const [urls, setUrls] = useState({});
// this should be within a function, better a useEffect with
// an async function defined inside and then called within the effect
var i;
var urlCount = 7;
// set fetching state
this.setState({ isFetching: true });
// loop (btw not recommended with async, better go with a map, return promises and then await Promise.all(yourArray))
for (i = 0; i < urlCount; i++) {
var response = await axios.get(USER_SERVICE_URL);
this.setState((oldState) => ({ ...oldState, [`url${i}`]: response.data[0] }));
}
// now you could access
const { url0, url1, url2... } = urls;
This is a suggestion or a tip rather than an answer
The state variable isFetching is used like a flag. You are using the setState method before fetching the data.
Calling this method will re-render the component.
Also it is called in a for loop. So the component will re-render multiple times.
My suggestion is avoid setting state unnecessarily. It will re-render the components and cause unexpected behavior.
Try this code
var i;
var urlCount = 7;
//If you set state inside a for loop and urlCount is a large value
//your component will render many times unnecessarily
this.setState({...this.state, isFetching: true});
for (i=0; i < urlCount; i++) {
var response = await axios.get(USER_SERVICE_URL);
this.image = response.data[0];
url[i] = this.image.url;
console.log([i] + url[i]);
}

How do I set a delay on a file upload in React?

I want to batch my records for uploading so I don't create server issues. I would like to be able to push 10 records at a time, every five seconds, just to prove the concept for now. I've put setInterval functions all over my code but can't get it to run at the right time. I've been at this for days but can't figure it out.
chunkData(data) {
const maxRecords = 10;
const loops = (data.length % maxRecords > 0) ? Math.floor(data.length / maxRecords) + 1 : data.length / maxRecords;
//console.log('data: ', data);
//console.log('loops: ', loops);
//setInterval(() => {
for (let loop = 0; loop < loops; loop++) {
console.log('loop: ', loop);
let start = loop * maxRecords;
//setInterval(() => {
for (let batch = start; batch < start + maxRecords; batch++) {
// the line below will become the upload function once I get this to work
if (data[batch] !== undefined) console.log('data[batch]: ', data[batch]);
}
//start = start + 10;
//}, 5000);
}
//}, 5000);
}
I'm certain it's a simple tweak I need but I'm clueless as to how to make it happen right now. Any advice would be greatly appreciated.
The bigger problem you'll have to figure out is that a client-side change will not help in this case. If you're trying to help your server this way, what happens when there is more than 1 concurrent user uploading? 2? 3? 100? 1000? This solution isn't scalable. You'll eventually (or very quickly) have to make sure your server is robust enough to handle upload traffic.
As for your specific code. Your problem is that you're using setInterval inside a for-loop but use the same value. Remember, uploading (or any XHR/fetch request) is an asynchronous action. Right now, you're setting the intervals to run at basically the same time.
To get actual intervals between uploads, you'd need something like this:
for (let loop = 0; loop < loops; loop++) {
console.log('loop: ', loop);
let start = loop * maxRecords;
for (let i=1, batch = start; batch < start + maxRecords; i++, batch++) {
// the line below will become the upload function once I get this to work
if (data[batch] !== undefined) {
setInterval(() => {
//make upload request here
}, (loop + 1) * i * 5000);
}
}
}
I'm not sure what your "start" variable is supposed to be.
In any case, this code is really error prone and fragile. I really advise reconsidering your approach and look into fixing your server side.
If you still wish to go with this client-side hack and even if not, and you're looking for a more stable client-side solution. I advise to go with react-uploady. It takes care of the uploads for you and all the edge cases that come with managing uploads in React.
You can even do your intervals easily:
import ChunkedUploady, { useChunkStartListener } from "#rpldy/chunked-uploady";
import UploadButton from "#rpldy/upload-button";
const CHUNK_SIZE = 1e+6;
const UploadButtonDelayedChunks = () => {
useChunkStartListener(() => new Promise((resolve) => {
//delays chunk upload by 5 seconds
setTimeout(resolve, 5000);
}));
return <UploadButton/>;
};
export const ChunkedExample = () => {
return (
<ChunkedUploady
destination={{ url: "https://my-server/upload" }}
chunkSize={CHUNK_SIZE}>
<UploadButtonDelayedChunks/>
</ChunkedUploady>
);
};

Why does it take so long for ng-click to work again?

EDIT:
ng-dbclick does not work for me or it would be used here.
Original:
I have a button. I click it once, it works as expected. I click it again, it does not work. I wait maybe a minute more, and then it does work. My function associated with the button does have lots of returns and if loops which call other functions, but the thing is, it does work perfectly each time it does. Here, I have implemented a double click function (hence the counter is 1 and 2, and it does work) - the project is marked as complete when double clicked, otherwise on a single click it is paused or started depending on its status.
I know I probably don't understand the digest or eval cycles here, or may need to use $scope.apply()...I basically don't understand those concepts at all or where to use them. I do have $http get and post requests in my nested functions.
Code from the HTML file:
<button ng-click="resumeorpauseproject(project, project.id)">Click me</button>
Code from the controller file:
$scope.resumeorpausecounter = 0;
$scope.resumeorpauseproject = function(value1, value2){
$scope.resumeorpausecounter = $scope.resumeorpausecounter + 1;
$timeout(function () {
if ($scope.resumeorpausecounter == 1) {
$scope.resumeorpauseproject1(value1, value2);
return;
}
if ($scope.resumeorpausecounter == 2) {
/*$scope.resumeorpausecounter = false;*/
$scope.markprojectascomplete(value1, value2);
return;
}
}, 300);
};
http://plnkr.co/edit/FNrP5Q9ivQR55zuLTa6U?p=preview
What you are trying to do is crazy imo.
Just model it properly and use ng-dblclick. You don't actually need to apply $digest nor $apply if understand your case correctly.
If we just think in terms of functionality, what you have is a project, and it should be marked as completed when doubleclicked, and should be toggled between started and stopped on single click:
Here is the relevant js:
project.state = {
started: false,
completed: false,
toggleStarted: function toggleStarted(){
this.started = !this.started;
},
markCompleted: function markCompleted(){
this.completed = true;
}
};
And here is the relevant html:
<button
data-ng-click="project.state.toggleStarted()"
data-ng-dblclick="project.state.markCompleted()"
>
Click me
</button>

Resources