now.getTime() inside useEffect doesn't work - javascript

I wanted to record how many time passed until user click the button.
My plan was like: (the time when user enter the page) - (the time when user click the button) = how many milliseoconds passed!
but my code had an bug and error. my useEffect didn't go what I wanted..
It didn't memorized the time when user entered the page.
console said :
👉undefined
and when first hit:
-1611798597788milliseconds passed
(it worked well as I planned when second hit. weird 🤔)
here's my code:
const UserTimer = () => {
const [startTime, setStartTime] = useState(null);
const [endTime, setEndTime] = useState(0);
const [diff, setDiff] = useState(0);
useEffect(() => {
let now = new Date();
setStartTime(now.getTime());
console.log('👉' + startTime);
}, []);
const onClick = () => {
setEndTime(Date.now());
setDiff(endTime - startTime);
setStartTime(endTime);
};
return (
<div>
<p>{diff}milliseconds passed</p>
<button onClick={onClick}>click here</button>
</div>
);
};
thank you in advance.

setState does not happen synchronously, so you will not see it change instantly. Best to set a variable then use that variable for your math.
Maybe something like this?
const UserTimer = () => {
const [startTime, setStartTime] = React.useState(null);
const [diff, setDiff] = React.useState(0);
React.useEffect(() => {
const now = Date.now()
setStartTime(Date.now());
console.log('👉' + now);
}, []);
const onClick = () => {
const endTime = Date.now()
setDiff(endTime - startTime);
setStartTime(endTime);
};
return (
<div>
<p>{diff}milliseconds passed</p>
<button onClick={onClick}>click here</button>
</div>
);
};

That's because your startTime state is set as null here:
const [startTime, setStartTime] = useState(null);
So first click it is still null, so your setDiff(endTime - startTime); returns the negative number. You can try to solve it using:
useEffect(() => {
let now = new Date();
setStartTime(now.getTime());
console.log('👉' + startTime);
}, [startTime]);

Related

Show countdown when holding button then show alert

I'm trying to create a button, which on hold shows a countdown of 3 seconds, if kept on hold for 3 seconds it shows an alert, but for some reason the countdown doesn't reset properly AND the alert fires up anyway, at every click (after the timeout)
my code is:
const [showCountdown, setShowCountdown] = useState(false)
const [holdTimer, setHoldTimer] = useState(3)
var timer = 0, interval;
const refreshDown = () => {
setShowCountdown(true)
interval = setInterval(function(){
setHoldTimer((t) => t - 1)
},1000);
timer = setTimeout(function(){
if (confirm('Refresh page?')){
window.location.reload()
}
},3000)
}
const refreshUp = () => {
clearTimeout(timer)
clearInterval(interval)
setShowCountdown(false)
setHoldTimer(3)
}
my html button has these two:
<svg onMouseDown={() => refreshDown()} onMouseUp={() => refreshUp()}>
...
</svg>
Have you tried with useRef ?
const timer = useRef();
const interval = useRef();
const refreshDown = () => {
setShowCountdown(true);
interval.current = setInterval(function () {
setHoldTimer((t) => t - 1);
}, 1000);
timer.current = setTimeout(function () {
if (confirm("Refresh page?")) {
window.location.reload();
}
}, 3000);
};
const refreshUp = () => {
clearTimeout(timer.current);
clearInterval(interval.current);
setShowCountdown(false);
setHoldTimer(3);
};
React component is rerendered each time state or props are changed. When setHoldTimer is executed, it changes the state. It causes reexecuttion of component’s code, so local variables “timer” and “interval” are declared again with values “0” and “undefined”, also new “refreshUp” function is created referencing new “timer” and “interval”. When you release the mouse, new “refreshUp” is called, interval and timeout are not cleared.
Try to define timer and interval as a state or with “useRef”. This way they will not be redefined during rerender.

how to use single save bar on multiple input fields - react js

image of save bar and input fields
I am new to react, I have a problem I want to display a Savebar on [onChange] on a number of input fields.
When input length is greater than 0 then it will show, but I have multiple input, my problem is when I type in 2 0r 3 fields it shows multiple time, but I want to show it by conditions that, when it is already open then it cannot open again. thanks in advance.
const [showBanner, setShowBanner] = useState(false);
const handelBanner = (e) => {
if (e.target.value.length > 0) {
setShowBanner(true)
}else{
setShowBanner(false)
}
}
return:
{showBanner ? (
<> save bar </>
) : null}
You can save all the input value to the state. Watch all input change and do your conditional logic in useEffect. Like this:
const [showBanner, setShowBanner] = useState(false);
const [input1, setInput1] = useState('');
const [input2, setInput2] = useState('');
useEffect(() => {
if () { // put your condition
setShowBanner(true);
} else {
setShowBanner(false)
}
}, [input1, input2])
const handleChangeInput1 = e => setInput1(e.target.value);
const handleChangeInput2 = e => setInput2(e.target.value);

Know how many promise are pending

My problem is the following :
I have an array of ids that I need to map to some database Ids using an HTTP request to my API like HTTP GET /foo/{id}
I need to wait for all values to show the data in my application
I'm currently doing this the following
async getValuesByIds({}, ids){
const valuesPromise = ids.map(async (id) => this.$axios.$get(`/foo/${id}`))
return Promise.all(valuesPromise)
}
And in the code before printing the values :
this.loading = true
this.getLeadsValuesByIds(idsArray).then(data => {
this.loading = false
this.values = data
})
The code is working fine but takes some times to run if i have a lot of ids.
In general, the first request ended in about 0.5 seconds and depending on the number of request, the last one can go up to 4 to 5 seconds
My goal here is to display a loading text informating the user how many request are left and how many are done.
Here is a short example using the jsonPlaceHolder API.
Basically what i want to have is instead of loading.. The number of request left (like {n} / 99 loaded
const loadData = () => {
const ids = Array.from({length: 99}, (_, i) => i + 1)
updateDataText('loading....')
const dataPromise = ids.map(async(id) => {
const post = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
return post.data
})
Promise.all(dataPromise).then(res => {
updateDataText(JSON.stringify(res))
})
}
const updateDataText = (text) => {
const div = document.getElementById('dataText')
div.innerText = text
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
<button onclick="loadData()">LoadData</button>
<p id="dataText"></p>
Note : I'm using Nuxt, i don't know if that change something.
You could use a <progress> tag like this
I've also added inflight and finished counter if you prefer that
const loadData = () => {
const ids = Array.from({length: 99}, (_, i) => i + 1)
let nStarted = 0, nFinished=0;
const inflight = document.getElementById('inflight');
const finished = document.getElementById('finished');
const progress = document.getElementById('progress');
progress.max = ids.length;
updateDataText('loading....')
const dataPromise = ids.map(async(id) => {
nStarted++;
inflight.textContent = nStarted - nFinished;
const post = await axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
progress.value++;
nFinished++;
finished.textContent = nFinished;
inflight.textContent = nStarted - nFinished;
return post.data
})
Promise.all(dataPromise).then(res => {
updateDataText(JSON.stringify(res, null, 4))
})
}
const updateDataText = (text) => {
const div = document.getElementById('dataText')
div.innerText = text
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
<button onclick="loadData()">LoadData</button>
<progress id="progress" value=0></progress>
<div>In Flight: <span id="inflight"></span></span>
<div>Finished: <span id="finished"></span></span>
<pre id="dataText"></pre>

Update DOM elements when state is updated using setTimeout() React

I have the following code in React. This code uses a time expensive function that performs some coputations over a large number of data. in the begining the state is filled with an array that says no computations. When the button is pressed, I want that array of the state to be filled with waiting, and after each iteration over i is complete, the element on potition i to be 'Done'. The problem is that by using set timeout, the DOM elements are not updating untill the whole function is over. Ignore the computations in the for, they are meaningless in this example. To have an ideea the function performs polynomial fit over the given data. My problem is how can I update the Dom elements while the function is running, So i can knwow which data is done and which is computing in real time
const Component = ( {data1, data2} ) => {
const [state, setState] = useState(Array(data1.length).fill('No Computations'))
const timeExpensiveFunction = () => {
// data1.length = 10
let variable0
let variable1
let variable2
let variable3
for(let i = 0; i< data1.length; i++){
//data1[i].length = 40 000
for(let j = 0; j< data1[i].length; j++){
variable1 += data1[i][j]
variable2 += variable1*data2[i][j]**3
variable3 += data2[i][j]**2
variable1 += data1[i][j]*data2[i][j]
}
setTimeout(() => {
setState(state => {
return[
...state.slice(0,i),
'Done',
...state.slice(i+1),
]
})
},1000)
}
return([variable1,variable2,variable3,variable4])
}
const randomFunc = (e) => {
setTimeout((e) => setState(Array(data1.length).fill('Waiting ...')),1000)
timeExpensiveFunction() //Duration depends on the number of data, it is usually higher that 10-15 seconds
}
return (
<div>
<div>
{state.map((element) => (
{element} //This does not update with the function setTimeout. It sets after the expensive computation is over
))}
</div>
<div>
<button onClick = {(e) => randomFunc(e)}>press</button>
</div>
</div>
)
}
First of all, You must use useState and useEffect to set a state and re-render the DOM.
You can use like below:
import { useState, useEffect } from 'react'
const Component = () => {
const [state, setState] = useState([1,2,3,4,5,6,7,8,9,10])
const [isPress, setPress] = useState(false); // use it to not call a method on first render
const timeExpensiveFunction = () => {
// lot of work here
}
const randomFunc = (e) => {
setState([10,9,8,7,6,5,4,3,2,1])
setPress(true)
}
useEffect({
if (isPress) setTimeout(() => timeExpensiveFunction(),1000)
}, [state])
return (
<div>
<div>
{state.map((element) => (
{element}
))}
</div>
<div>
<button onClick = {(e) => randomFunc(e)}>press</button>
</div>
</div>
)
}

Cannot empty array in ReastJS useEffect hook

I'm having this table view in React where I fetch an array from API and display it. When the user types something on the table search box, I'm trying to clear the current state array and render the completely new result. But for some reason, the result keeps getting appended to the current set of results.
Here's my code:
const Layout = () => {
var infiniteScrollTimeout = true;
const [apiList, setapiList] = useState([]);
//invoked from child.
const search = (searchParameter) => {
//Clearing the apiList to load new one but the console log after setApiList still logs the old list
setapiList([]); // tried setApiList([...[]]), copying the apiList to another var and emptying it and then setting it too.
console.log(apiList); //logs the old one.
loadApiResults(searchParameter);
};
let url =
AppConfig.api_url + (searchParameter || "");
const loadApiResults = async (searchParameter) => {
let response = await fetch(url + formurlencoded(requestObject), {
method: "get",
headers: headers,
});
let ApiResult = await response.json(); // read response body and parse as JSON
if (ApiResult.status == true) {
//Set the url for next fetch when user scrolls to bottom.
url = ApiResult.next_page_url + (searchParameter || "");
let data;
data = ApiResult.data;
setapiList([...data]);
}
}
useEffect(() => {
loadApiResults();
document.getElementById("myscroll").addEventListener("scroll", () => {
if (
document.getElementById("myscroll").scrollTop +
document.getElementById("myscroll").clientHeight >=
document.getElementById("myscroll").scrollHeight - 10
) {
if (infiniteScrollTimeout == true) {
console.log("END OF PAGE");
loadApiResults();
infiniteScrollTimeout = false;
setTimeout(() => {
infiniteScrollTimeout = true;
}, 1000);
}
}
});
}, []);
return (
<ContentContainer>
<Table
...
/>
</ContentContainer>
);
};
export default Layout;
What am I doing wrong?
UPDATE: I do see a brief moment of the state being reset, on calling the loadApiResult again after resetting the state. The old state comes back. If I remove the call to loadApiResult, the table render stays empty.
add apiList in array as the second parameter in useEffect
You need to use the dependencies feature in your useEffect function
const [searchParameter, setSearchParameter] = useState("");
... mode code ...
useEffect(() => {
loadApiResults();
... more code ...
}, [searchParameter]);
useEffect will automatically trigger whenever the value of searchParameter changes, assuming your input uses setSearchParameter on change

Categories

Resources