useLazyQuery onCompleted doesn't trigger - javascript

I am running the query every 1 minute to check the progress.
let timer = null
const [inProgress, setInProgress] = useState(false)
const [
checkProgress,
{ loading2, data2 }
] = useLazyQuery(CHECK_PROGRESS, {
fetchPolicy: 'cache-and-network',
onCompleted: (data) => {
if(data.progress.completed) {
setInProgress(false)
// some code
} else {
// some code
}
}
}
)
useEffect(() => {
if (inProgress) {
timer = setInterval(() => {
checkProgress()
}, 1000 * 60)
} else {
clearInterval(timer)
}
return () => {
clearInterval(timer)
}
}, [inProgress])
onCompleted doesn't trigger once data.progress.completed is changed from false to true.
But the query is still running every 1 minute.
Any idea to fix this?

The issue was on the backend. I was setting null for non-null field.
I found the error after adding onError callback.
onError: (error) => {
console.log(error)
}

Related

setInterval keeps running even after clearInterval

Thanks in advance.
This is my question : It is Quiz website for couple.
Before partner finish quiz, It send get request to server every 5s.
but the problem is even partner's answers are set, setInterval never stops.
but if I refresh my website, It works well.
Can you please give me advise?
const postAnswers = useGetResults();
const postPartnerAnswers = useGetPartnerResults();
const [myResult, setMyResult] = useState<FinalAnswer | undefined>();
const [partnerResult, setPartnerResult] = useState<FinalAnswer | undefined>();
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(false);
const init = async () => {
try {
const email = localStorage.getItem('email');
const partnerEmail = localStorage.getItem('partnerEmail');
if (email !== undefined && partnerEmail !== undefined) {
// localStorage에 이메일 값들이 있으면,
const result = await postAnswers(email, partnerEmail);
const otherResult = await postPartnerAnswers(email, partnerEmail);
if (result.answers !== undefined && otherResult.answers !== undefined) {
// 몽고디비에서 받아온 값이 둘다 있으면
setMyResult(result);
setPartnerResult(otherResult);
} else {
// 몽고디비에서 받아온 값이 없으면
console.log(result.answers, otherResult.answers);
setIsLoading(true);
}
}
} catch (error) {
setErrorMessage('로딩하는 도중 에러가 발생했습니다');
console.error(error);
}
};
useEffect(() => {
init();
}, []);
useEffect(() => {
if (myResult !== undefined && partnerResult !== undefined) {
setIsLoading(false);
console.log('둘다 값이 있어요!');
console.log(isLoading);
}
}, [myResult, partnerResult]);
const timer = () => {
return setInterval(() => {
init();
console.log('isLoading', isLoading);
if (isLoading === false) {
console.log('clear');
clearInterval(timer());
}
}, 5000);
};
useEffect(() => {
if (isLoading === true) {
console.log('둘다 값이 없어요!');
timer();
}
if (isLoading === false) {
console.log('clear');
clearInterval(timer());
}
}, [isLoading]);
deployed website : https://www.couple-quiz.com/
Expanding on #Ethansocal comment:
Your code is calling clearInterval(timer()) which will create a new interval that it will immediately clear. It seems that you are confusing the API of removeEventListener and clearInterval.
clearInterval should be called with the identifier returned by setInterval.
I suggest getting rid of the timer function and rewriting your last useEffect to make it return a cleanup function when isLoading is true:
useEffect(() => {
if (isLoading) {
console.log('둘다 값이 없어요!');
const interval = setInterval(init, 5_000);
return () => { clearInterval(interval) };
} else {
console.log('clear');
}
}, [isLoading]);

how to show multiple message using react hooks? [duplicate]

This question already has answers here:
Wrong React hooks behaviour with event listener
(9 answers)
Closed 17 days ago.
I am trying to show multiple message using provider and hooks . But I am not able to show multiple message .One one message is show at one time don't know why ?
here is my code
https://codesandbox.io/s/new-mountain-cnkye5?file=/src/App.tsx:274-562
React.useEffect(() => {
setTimeout(() => {
utilContext.addMessage("error 2 sec");
}, 300);
setTimeout(() => {
utilContext.addMessage("error 5 mili sec");
}, 2000);
setTimeout(() => {
utilContext.addMessage("error 1 sec");
}, 1000);
}, []);
I am also using map function to render all message.
return (
<>
{messages.map((msg, index) => (
<div key={`Toast-Message-${index}`}>
{msg.msg}
<button
onClick={(event) => {
alert("000");
}}
>
close
</button>
</div>
))}
<ConfirmationDialogContext.Provider value={value}>
{children}
</ConfirmationDialogContext.Provider>
</>
);
Expected output : It will show 3 messages after some time.
Change your
const addMessage = (message: string, status: "success" | "error") => {
setmessages([...messages, { msg: message, type: status, duration: 5000 }]);
};
to
const addMessage = (message: string, status: "success" | "error") => {
setmessages((currentMessages) => [
...currentMessages,
{ msg: message, type: status, duration: 5000 }
]);
};
This is because you call the 3 addMessage in the same time, and so the messages variable has the same value in all three calls.
Read Updating state based on the previous state for more info on this syntax
You context is changing every time you add a message, and in your useEffect dependencies you don't have the context as a dep. which means that you're only adding messages to the first context instance, while rendering the latest one all the time.
BUT, if you add the context to your useEffect dependencies you will get an infinite loop.
One (Bad) solution would be to track every setTimeout with useRef like this:
export default function App() {
const utilContext = useConfirmationDialog();
const m1Ref = React.useRef(false);
const m2Ref = React.useRef(false);
const m3Ref = React.useRef(false);
React.useEffect(() => {
console.log("useEffect");
const s = [];
if (m1Ref.current === false) {
const s1 = setTimeout(() => {
utilContext.addMessage("error 300 msec");
}, 300);
s.push(s1);
m1Ref.current = true;
}
if (m2Ref.current === false) {
const s2 = setTimeout(() => {
m2Ref.current = true;
utilContext.addMessage("error 2 sec");
}, 2000);
s.push(s2);
}
if (m3Ref.current === false) {
const s3 = setTimeout(() => {
m3Ref.current = true;
utilContext.addMessage("error 1 sec");
}, 1000);
s.push(s3);
}
return () => {
s.forEach((x) => clearTimeout(x));
};
}, [utilContext]);
return (
<Typography>
MUI example. Please put the code to reproduce the issue in src/App.tsx
</Typography>
);
}
I think that you should move the delay of presenting the messages out of the sender into the context, like this:
const addMessage = (delay: number, message: string, status: "success" | "error") => {
setTimeout(() => {
setmessages((currentMessages) => [
...currentMessages,
{ msg: message, type: status, duration: 5000 }
]);
}, delay);
};

Rendered fewer hooks than expected. This may be caused by an accidental early return statement

I'm getting this error when triggering a setState inside of a custom React hook. I'm not sure of how to fix it, can anyone show me what I'm doing wrong. It is getting the error when it hits handleSetReportState() line. How should I be setting the report state from inside the hook?
custom useinterval poll hook
export function usePoll(callback: IntervalFunction, delay: number) {
const savedCallback = useRef<IntervalFunction | null>()
useEffect(() => {
savedCallback.current = callback
}, [callback])
useEffect(() => {
function tick() {
if (savedCallback.current !== null) {
savedCallback.current()
}
}
const id = setInterval(tick, delay)
return () => clearInterval(id)
}, [delay])
}
React FC
const BankLink: React.FC = ({ report: _report }) => {
const [report, setReport] = React.useState(_report)
if ([...Statues].includes(report.status)) {
usePoll(async () => {
const initialStatus = _report.status
const { result } = await apiPost(`/links/search` });
const currentReport = result.results.filter((item: { id: string; }) => item.id === _report.id)
if (currentReport[0].status !== initialStatus) {
handleSetReportState(currentReport[0])
console.log('status changed')
} else {
console.log('status unchanged')
}
}, 5000)
}
... rest
This is because you put usePoll in if condition, see https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
You can put the condition into the callback
usePoll(async () => {
if ([...Statues].includes(report.status)) {
const initialStatus = _report.status
const { result } = await apiPost(`/links/search` });
const currentReport = result.results.filter((item: { id: string; }) => item.id === _report.id)
if (currentReport[0].status !== initialStatus) {
handleSetReportState(currentReport[0])
console.log('status changed')
} else {
console.log('status unchanged')
}
}
}, 5000)
And if the delay will affect report.status, use ref to store report.status and read from ref value in the callback.

ClearInterval Not working in React Native

i'm try to clear interval when the user in not in the detail section and if match the status is 1, my code like this :
useEffect(() => {
console.log(section)
console.log('ini interval lo', interval)
if (section !== "detailCheckIn") {
clearInterval(interval);
console.log('aa', clearInterval(interval) ? true : false)
}
console.log('section di : ', section)
}, [section]);
const checkStatus = (data) => {
console.log('datanya nih ', data)
interval = setInterval(() => {
console.log('ini test interval', userToken)
consume.getWithParams('CheckinInfo', {}, { token }, { checkin_id: data })
.then(response => {
console.log('ini tuh response', response)
//ubah jadi 1 kalomau final test
if (response.result.status === 1) {
navigation.navigate('Service')
clearInterval(interval);
console.log('di clear')
AsyncStorage.setItem('isCheckIn', 'udah check in nih')
}
})
.catch(err => {
console.log(err)
console.log('token error', token)
})
}, 2000);
}
when i'm console log the clear interval, that return false
Try this. Notice the use of useRef to store a single reference to _interval, and how this is only ever changed through the functions clearCurrentInterval and replaceCurrentInterval.
If you only update your interval through these two functions, you can be sure you will only ever have one interval at a time, as your previous interval always gets cleared first.
const _interval = useRef(null);
const interval = () => _interval.current;
const clearCurrentInterval = () => {
clearInterval(interval());
};
const replaceCurrentInterval = (newInterval) => {
clearCurrentInterval();
_interval.current = newInterval;
};
useEffect(() => {
console.log(section);
console.log("ini interval lo", interval());
if (section !== "detailCheckIn") {
clearCurrentInterval();
}
console.log("section di : ", section);
}, [section]);
const checkStatus = (data) => {
console.log("datanya nih ", data);
const newInterval = setInterval(() => {
console.log("ini test interval", userToken);
consume
.getWithParams("CheckinInfo", {}, { token }, { checkin_id: data })
.then((response) => {
console.log("ini tuh response", response);
//ubah jadi 1 kalomau final test
if (response.result.status === 1) {
navigation.navigate("Service");
clearCurrentInterval();
console.log("di clear");
AsyncStorage.setItem("isCheckIn", "udah check in nih");
}
})
.catch((err) => {
console.log(err);
console.log("token error", token);
});
}, 2000);
replaceCurrentInterval(newInterval);
};
However, depending on how you're using this, you might be better off just having a useEffect that creates a single interval when the component mounts, and clears it when it unmounts or gets the API response you want.

How to stop setInterval() in React

I use setInterval() to send GET request for state updating. I also use clearInterval() after the update process complete.
//
// getSynProcessState used for updating data by sending GET request to an API after every minute
//
intervalID = 0;
getSynProcessState = () => {
// get total and current sync
this.intervalID = setInterval(() => {
axios.get('http://mySite/data/')
.then(res => {
console.log(res.data)
});
},1000);
}
//
// clearInterval() will run if this.state.isSyncStart === false
//
componentDidUpdate() {
if (this.state.isSyncStart) {
this.getSynProcessState() //setInterval()
console.log('componentDidUpdate: ' + this.state.isSyncStart)
} else {
clearInterval(this.intervalID)
console.log('componentDidUpdate: ' + this.state.isSyncStart)
}
}
As you can see that when [this.state.isSyncStart === true] => setInterval() run OK
But when [this.state.isSyncStart === false] => clearInterval() run but the GET requests keep sending
You are overwriting the current interval in your componentDidUpdate call. Do a check e.g.
if (this.state.isSyncStart) {
this.interValID == 0 && this.getSynProcessState() //setInterval()
console.log('componentDidUpdate: ' + this.state.isSyncStart)
} else {
clearInterval(this.intervalID)
console.log('componentDidUpdate: ' + this.state.isSyncStart)
}
I somehow solved the problem by adding runOnce and set it in the 'If' Condition. Maybe it prevent the overwriting on [this.intervalID]
runOnce = true
getSynProcessState = () => {
if (this.state.isSyncStart && this.runOnce) {
this.runOnce = false
this.intervalID = setInterval(() => {
axios.get('http://192.168.51.28:8031/process/')
.then(res => {
console.log(res.data)
// this.setState({
// total: res.data.total,
// current: res.data.current
// })
// console.log('1: ' +this.state.total)
});
},200);
} else {
clearInterval(this.intervalID)
}
}
componentDidUpdate() {
this.getSynProcessState()
}

Categories