how i can let the bot delete all messages in a specific channel then send's a message and loop this - discord.js

This piece of code deletes any message in a specific channel after 3 seconds
client.on("messageCreate", async message =>{
if (message.channel.id === '899644830848798755')
{
setTimeout(() => message.delete(), 3000)
}
}); 
What I want is that after 5 minutes, it will delete its own message and send a message without deleting it. Something like this:
client.on('ready', () => {
setInterval(() => {
message.channel.bulkDelete.then(() => {
client.channels.cache.get('899644830848798755').send('Hello here!')
}, 300000);
})
and can someone tell me what the difference between setTimeout() and setInterval() is?

You're using bulkDelete method, it will delete the message every 5 mins. You need to create a function to create your looping, you can also use setInterval aswell.
Using setInterval method:
client.on('ready', () => {
setInterval(() => {
const chan = client.channels.cache.get('899644830848798755');
chan.send('Hello here!')
}, 300000);
})
Using function method:
client.on('ready', () => {
async function repeat() { //First loop of function declined.
setTimeout(() => {
const chan = client.channels.cache.get('899644830848798755');
chan.send('Hello here!')
repeat(); //Second loop Accepted for loop
}, 300000)
}
repeat(); //First loop function accepted. Then ignore
})
SetInterval:
The setInterval() method, offered on the Window and Worker interfaces, repeatedly calls a function or executes a code snippet, with a fixed time delay between each call.
setInterval source link: Click Here
SetTimeout:
The global setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires.
setTimeout source link: Click Here

Related

Preventing a setState from running until network request is complete

I have the following function, inside of a Context file in my React app:
const fetchAll = (userId) => {
try {
fetchDetails(userId)
// To be clear... There's multiple functions here, i.e:
// fetchContact(userId)
} catch (err) {
console.log(err)
}
setPending(false)
}
I've removed some of the functions - but the main premise of the function is to combine multiple promises together, and until these are complete, display a 'pending' component.
This pending component is displayed if the 'pending' state is set to true:
const [pending, setPending] = useState(true)
However, at the moment, what is happening is that the try is attempted, but the setPending is executed at the same time.
I thought one way around this would be to utilise a 'finally' call at the end of the my try / catch, but that still executes at the same time. Like this:
const fetchAll = (userId) => {
try {
fetchDetails(userId)
} catch (err) {
console.log(err)
} finally {
setPending(false)
}
}
I don't want any of my functions to be run asynchronously: I want them all to execute at the same time to prevent a waterfall effect of multiple network requests at once.
For reference, my individual 'fetch' functions call an endpoint and set state data based upon the response:
const fetchDetails = (userId) => {
axios.post("/api/fetch/fetchDetails", {
id: userId
})
.then((response) => {
console.log(response.data)
setName(response.data.name)
setPreviewPhoto(response.data.profile_picture_url)
setPhotoName(response.data.profile_picture_name)
setPhotoFile(response.data.profile_picture_url)
})
}
Does anyone have any suggestions as to how I could make this work?
Let's assume you have 2 API calls: fetchAll('123') and fetchAll('321');
In order to wait for all of your requests and then update your state, you should use Promise.all like this:
Promise.all([fetchAll('123'), fetchAll('321')]).then((responses) => {
setPending(false)
}
fetchDetails returning a promise, you need to use async/await
const fetchAll = async (userId) => {
try {
await fetchDetails(userId)
} catch (err) {
console.log(err)
} finally {
setPending(false)
}
}
You can have multiple async calls using Promise.all() or Promise.allSettled() depending on your use case.
setPending(true)
try {
await Promise.all([() => fetchAll(1), () => fetchAll(2)])
} finally {
setPending(false)
}
This will wait for all calls to complete (or one to fail)

Refactoring useEffect to only require one database call

At the moment, I have a component which completes some backend calls to decide when to start displaying the UI.
It's structured like this:
useEffect(() => {
getData()
})
const getData = async () => {
await verifyUser()
await fetchData()
}
The purpose here, is that verifyUser() is supposed to run first, and in the response to verifyUser(), a user id is provided by the backend.
const verifyUser = async () => {
if (!localStorage.getItem('auth')) {
return
}
if (localStorage.getItem('auth')) {
await axios.post("/api/checkAuth", {
token: JSON.parse(localStorage.getItem('auth'))
})
.then((response) => {
return setUserId(response.data.user_id)
})
.catch((err) => {
console.log(err)
localStorage.removeItem('auth')
})
}
}
As a result of this, the fetchData() function is supposed to wait until the verifyUser() function has stopped resolving, so it can use the user id in the database query.
However, at the moment it...
Calls once, without the user id
Then calls again, with the user id (and therefore resolves successfully)
Here's the function for reference:
const fetchData = async () => {
console.log("Fetch data called.")
console.log(userId)
await axios.post("/api/fetch/fetchDetails", {
user_id: userId
})
.then((response) => {
// Sets user details in here...
return response
})
.then(() => {
return setFetching(false)
})
.catch((err) => {
console.log(err)
})
}
What I'm trying to achieve here is to essentially remove any concurrency and just run the functions sequentially. I'm not 100% sure what the best practice here would be, so some feedback would be appreciated!
Your useEffect is missing a dependency array argument:
useEffect(() => {
getData()
})
should be:
useEffect(() => {
getData()
}, [])
Without that argument, useEffect will run once each time your component renders. With that argument, it will only run once, when the component is first mounted (ie. after the first render).
If you needed it to depend on another variable (eg. user.id isn't defined on load, but is later on) you could put that variable in the dependency array, ie.
useEffect(() => {
if (!user.id) return;
getData()
}, [user.id])
This version would run once when the component is mounted, then again if the user.id changes (eg. if it goes from null to an actual number).
In React, the useEffect hook accepts two arguments - the first one is a function (this is the "effect"), and the second one is a dependency array. The simplest useEffect hook looks like this:
useEffect(() => {
}, [])
The above hook has no dependency (because the array is empty), and runs only when the component initially mounts, and then goes silent.
If you don't pass in a dependency array as the second argument, as #machineghost said, the hook will run the "effect" function every time your component re-renders.
Now to your specific problem. You want to run fetchData after verifyUser has resolved its Promise, so you'd add the outcome of verifyUser as a dependency to a separate useEffect hook that calls fetchData. In this case, the outcome is setting userId.
So instead of this:
useEffect(() => {
getData()
})
const getData = async () => {
await verifyUser()
await fetchData()
}
Do this:
useEffect(() => {
verifyUser();
}, []);
useEffect(() => {
if (userId) { // assuming userId has a false-y value before verifyUser resolved
await fetchData();
}
}, [userId])

How i can await the result of the AsyncStorage?

i need to run the AsyncStorage first before run the other functions , but AsyncStorage take a time to run and it give the resutlt in the end of the code
constructor(props) {
super(props);
AsyncStorage.getItem(TOKEN).then((r) => {
console.log("AsyncStorage Function")
})
this.state = { token: null }
console.log("The last Function")
}
Ideally you shouldn't be performing any async tasks inside a constructor assuming this is a react class component.
So if you wish to perform any async tasks you could make use of componentDidMount from the component lifecycle method.
But in general if you want to wait for the async operation then this can be achieved in different ways
Using Promises
const promiseFun = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Success")
}, 1000)
})
}
promiseFun().then((res) => {
//here res will be what is returned from the promise function
console.log(res);
//All the required things that needed to be perfomed after the async operation should be placed here.
});
//Whatever the actions/statements written here will be executed before the async operation is finished
console.log("Before Promise Returned");
Using async/await
const promiseFun = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Success")
}, 1000)
})
}
const execFun = async() => {
let res = await promiseFun();
//All the statments from here will be executed only after the promise/async function returned.
console.log(res);
}
execFun();
//All the statements here will be executed without waiting for the promise/async function gets executed
console.log("Before Promise Returned");

Call API every 30 seconds in ReactJS [duplicate]

I have to monitoring some data update info on the screen each one or two seconds.
The way I figured that was using this implementation:
componentDidMount() {
this.timer = setInterval(()=> this.getItems(), 1000);
}
componentWillUnmount() {
this.timer = null;
}
getItems() {
fetch(this.getEndpoint('api url endpoint'))
.then(result => result.json())
.then(result => this.setState({ items: result }));
}
Is this the correct approach?
Well, since you have only an API and don't have control over it in order to change it to use sockets, the only way you have is to poll.
As per your polling is concerned, you're doing the decent approach. But there is one catch in your code above.
componentDidMount() {
this.timer = setInterval(()=> this.getItems(), 1000);
}
componentWillUnmount() {
this.timer = null; // here...
}
getItems() {
fetch(this.getEndpoint('api url endpoint'))
.then(result => result.json())
.then(result => this.setState({ items: result }));
}
The issue here is that once your component unmounts, though the reference to interval that you stored in this.timer is set to null, it is not stopped yet. The interval will keep invoking the handler even after your component has been unmounted and will try to setState in a component which no longer exists.
To handle it properly use clearInterval(this.timer) first and then set this.timer = null.
Also, the fetch call is asynchronous, which might cause the same issue. Make it cancelable and cancel if any fetch is incomplete.
I hope this helps.
Although an old question it was the top result when I searched for React Polling and didn't have an answer that worked with Hooks.
// utils.js
import React, { useState, useEffect, useRef } from 'react';
export const useInterval = (callback, delay) => {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
Source: https://overreacted.io/making-setinterval-declarative-with-react-hooks/
You can then just import and use.
// MyPage.js
import useInterval from '../utils';
const MyPage = () => {
useInterval(() => {
// put your interval code here.
}, 1000 * 10);
return <div>my page content</div>;
}
You could use a combination of setTimeout and clearTimeout.
setInterval would fire the API call every 'x' seconds irrespective whether the previous call succeeded or failed. This can eat into your browser memory and degrade performance over time. Moreover, if the server is down, setInterval would continue to bombard the server not knowing its down status.
Whereas,
You could do a recursion using setTimeout. Fire a subsequent API call, only if the previous API call succeed. If previous call has failed, clear the timeout and do not fire any further calls. if required, alert the user on failure. Let the user refresh the page to restart this process.
Here is an example code:
let apiTimeout = setTimeout(fetchAPIData, 1000);
function fetchAPIData(){
fetch('API_END_POINT')
.then(res => {
if(res.statusCode == 200){
// Process the response and update the view.
// Recreate a setTimeout API call which will be fired after 1 second.
apiTimeout = setTimeout(fetchAPIData, 1000);
}else{
clearTimeout(apiTimeout);
// Failure case. If required, alert the user.
}
})
.fail(function(){
clearTimeout(apiTimeout);
// Failure case. If required, alert the user.
});
}
#AmitJS94, there's a detailed section on how to stop an interval that adds onto the methods that GavKilbride mentioned in this article.
The author says to add a state for a delay variable, and to pass in "null" for that delay when you want to pause the interval:
const [delay, setDelay] = useState(1000);
const [isRunning, setIsRunning] = useState(true);
useInterval(() => {
setCount(count + 1);
}, isRunning ? delay : null);
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
Definitely read the article to get a better understanding of the details -- it's super thorough and well-written!
As Vasanth mention, I preferred to:
use setTimeout to measure the time between the end of the last request and the beginning of the next one
make the first request straight away, not after the delay
inspired by the answer from #KyleMit https://stackoverflow.com/a/64654157/343900
import { useEffect, useRef } from 'react';
export const useInterval = (
callback: Function,
fnCondition: Function,
delay: number,
) => {
const savedCallback = useRef<Function>();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
let id: NodeJS.Timeout;
const tick = async () => {
try {
const response =
typeof savedCallback.current === 'function' &&
(await savedCallback.current());
if (fnCondition(response)) {
id = setTimeout(tick, delay);
} else {
clearTimeout(id);
}
} catch (e) {
console.error(e);
}
};
tick();
return () => id && clearTimeout(id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [delay]);
};
WORKS: Using fnCondition inside which can be a condition based on the response from the last request.
//axios-hooks
const {
data,
isLoadingData,
getData,
} = api.useGetData();
const fnCondition = (result: any) => {
const randomContidion = Math.random();
//return true to continue
return randomContidion < 0.9;
};
useInterval(() => getData(), fnCondition, 1000);
DOES NOT WORK: Passing delay as null to stop useInterval like this does not work for me
with this code: https://www.aaron-powell.com/posts/2019-09-23-recursive-settimeout-with-react-hooks/
(You might get the impression it works, but after a few starts/stops it breaks)
const [isRunning, setIsRunning] = useState(true);
const handleOnclick = () => {
setIsRunning(!isRunning);
};
useInterval(() => getData(), isRunning ? 1000 : null);
<button onClick={handleOnclick}>{isRunning ? 'Stop' : 'Start'}</button>
Sum up: I'm able to stop useInterval by passing fnCondition, but not by passing delay=null
Here's a simple, full solution, that:
Polls every X seconds
Has the option of increasing the timeout each time the logic runs so you don't overload the server
Clears the timeouts when the end user exits the component
//mount data
componentDidMount() {
//run this function to get your data for the first time
this.getYourData();
//use the setTimeout to poll continuously, but each time increase the timer
this.timer = setTimeout(this.timeoutIncreaser, this.timeoutCounter);
}
//unmounting process
componentWillUnmount() {
this.timer = null; //clear variable
this.timeoutIncreaser = null; //clear function that resets timer
}
//increase by timeout by certain amount each time this is ran, and call fetchData() to reload screen
timeoutIncreaser = () => {
this.timeoutCounter += 1000 * 2; //increase timeout by 2 seconds every time
this.getYourData(); //this can be any function that you want ran every x seconds
setTimeout(this.timeoutIncreaser, this.timeoutCounter);
}
Here is a simple example using hooks in function component and this will refresh your data in a set interval.
import React from 'react';
import { useEffect, useState } from 'react';
export default function App() {
let [jokes, setJokes] = useState('Initial');
async function fetchJokes() {
let a = await fetch('https://api.chucknorris.io/jokes/random');
let b = await a.json();
setJokes(b.value);
}
// Below function works like compomentWillUnmount and hence it clears the timeout
useEffect(() => {
let id = setTimeout(fetchJokes, 2000);
return () => clearTimeout(id);
});
return <div>{jokes}</div>;
}
or, you can use axios as well to make the API calls.
function App() {
const [state, setState] = useState("Loading.....");
function fetchData() {
axios.get(`https://api.chucknorris.io/jokes/random`).then((response) => {
setState(response.data.value);
});
}
useEffect(() => {
console.log("Hi there!");
let timerId = setTimeout(fetchData, 2000);
return ()=> clearInterval(timerId);
});
return (
<>
This component
<h3>{state}</h3>
</>
);
}

How can I use an effect once, but still use state, with React Hooks?

I am trying to set an interval on mount/unmount only, but allow the callback to have one variable from state.
I have code like this:
const [cachedData, setCachedData] = useState(false);
async function refreshData() {
const data = await axios('http://www.example.com/');
setCachedData(data);
}
useEffect(() => {
let interval;
async function fetch() {
await refreshData();
interval = setInterval(refreshData,5000);
console.log('set interval to', interval)
}
fetch();
return () => {
console.log('clearing interval', interval);
clearInterval(interval);
}
}, []);
I am running into a catch-22. The second argument of useEffect says what variables to pay attention to. I've read making it an empty array makes this only mount/unmount rather than on any state updates. The issue I've found is that doing that, means refreshData has no access to cachedData, so I can't know I have valid data (to avoid the XHR request for an amount of time). If I pass cachedData into second argument of useEffect, it will have the variable but run more than it should. Not sure of a way around this.
I should note that if I pass cachedData to the second arg, and console.log by the clear and the setting of the interval, my console outputs something like this:
clearing interval undefined
set interval to 5
set interval to 7
So it seemingly runs the unmount and then the effect twice over without clearing again. This causes a double axios call.
You can use a ref to get at the current cachedData with something like the following:
const [cachedData, setCachedData] = useState(false);
const cachedDataRef = useRef(cachedData);
useEffect(() => {
// Update ref in effect so that render has no side effects.
cachedDataRef.current = cachedData;
}, [cachedData]);
useEffect(() => {
async function refreshData() {
if (cachedDataRef.current) {
// do something different
} else {
const data = await axios('http://www.example.com/');
setCachedData(data);
}
}
let interval;
async function fetch() {
await refreshData();
interval = setInterval(refreshData,5000);
console.log('set interval to', interval)
}
fetch();
return () => {
console.log('clearing interval', interval);
clearInterval(interval);
}
}, []);

Resources