I'm trying to test a props.onChange function in react. I can't seem to test these few lines. Anyone have any suggestions.
prop.onChange.js
if (response.data === false) {
setTimeout(function () {
props.onChange(props.props.className = ["privacyPolicyClipboardAgreement"]);
}, 1000);
}
props.onChange.test.js
it('tests that useEffect is called when the user first logs in to check if the user has agreed to the privacy agreement .', async () => {
console.log("Test started for the useEffect block.");
jest.useFakeTimers();
jest.spyOn(React, 'useEffect').mockImplementationOnce((f) => f());
await render(<PrivacyPolicyForRegistration className={props.className} props={props} onChange={jest.fn()} />);
expect(axiosMockPrivacyAgreed.post).toHaveBeenCalledTimes(1);
expect(axiosMockPrivacyAgreed.post).toHaveBeenCalledWith("privacyAgreed", { "email": undefined }, { "headers": { "userpassword": undefined } });
//axiosMockPrivacyAgreed seems to come from axiosMockFeedback for some reason.
const response = await axiosMockPrivacyAgreed.post();
console.log("This is the response in the jest test: " + JSON.stringify(response.data));
if (response.data === false) {
console.log("This is triggered if the statement is true.");
await waitFor(
() => {
expect(setTimeout).toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 1000);
},
{ timeout: 1000 },
);
}
});
The setTimeOut line seems to show test coverage, but everything in the setTimeout block and after doesn't have coverage. Does anyone have any thoughts?
Related
In the code snippet you see, I am trying to reach the data that I have determined through asyncStorage in the getToken and `` functions, but when I open the page with these codes from the emulator, the data is empty for the first time, and then when I do ctrl+s from the editor, the data is full. What is the reason for this problem?
App.js Page
getToken: async () => {
const token = AsyncStorage.getItem('userToken');
return token;
},
getMail: async () => {
const mail = AsyncStorage.getItem('userMail');
return mail;
},
OrderListScreen Page
getToken().then((res) => {
if(res){
setToken(res);
console.log(token)
}else {
setToken('');
}
});
getMail().then((res) => {
if(res){
setMail(res);
console.log(mail)
}else {
setMail('');
}
});
Apply await before using AsyncStorage.getItem:
getToken: async () => {
const token = await AsyncStorage.getItem('userToken');
return token;
},
getMail: async () => {
const mail = await AsyncStorage.getItem('userMail');
return mail;
},
In the log you'll not get the updated state in next line of state setter.
getToken().then((res) => {
if(res){
setToken(res);
console.log(token); //You'll never get this value here because state updates are asynchronous in React
console.log("res : ", res);
}else {
setToken('');
}
});
getMail().then((res) => {
if(res){
setMail(res);
console.log(mail)//You'll never get this value here because state updates are asynchronous in React
console.log("Email Res : ", res);
}else {
setMail('');
}
});
I need to pass 2 functions to my useEffect -
one that renders the whole page and sends some general data to the server,
another one is voting function and sends vote results (called "answer") to the server.
I tried to put both functions inside one useEffect, as well as using useEffect twice. Will show code for both of them.
2 useEffects was working for a little bit, then I started getting this error: "Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function".
So, my question is:
Can I call 2 functions under one useEffect or can I have 2 useEffects?
How to get rid of my unmount error? I tried setting isMounted to true/false but still no luck.
Code for both functions in one useEffect:
useEffect(() => {
let isMounted = true;
function viewingPoll() {
fetch(`/api/polls/${pollIview}`)
.then((resp) => resp.json())
.then((result) => {
if (isMounted) {
console.log("my result on pollvoter is", result);
if (result.noSuchPoll) {
setPoll(false);
} else {
setPoll(result.pollInfo);
console.log("result right here", result); //all the happy points
}
}
})
.catch((err) => {
console.log("error in pollvoter.js ", err);
this.setState({
error: true,
});
});
}
viewingPoll();
function voting() {
let isMounted = true;
fetch("/api/vote", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
poll_id: poll.id,
answer: answer,
}).then(() => {
if (isMounted) {
// location.redirect(`/polls/${result.pollId}`);
location.reload();
}
}),
});
}
voting();
return () => {
isMounted = false;
};
}, [answer]);
Example 2: having 2 separate useEffects
useEffect(() => {
let isMounted = true;
function viewingPoll() {
fetch(`/api/polls/${pollIview}`)
.then((resp) => resp.json())
.then((result) => {
if (isMounted) {
console.log("my result on pollvoter is", result);
if (result.noSuchPoll) {
setPoll(false);
} else {
setPoll(result.pollInfo);
console.log("result right here", result); //all the happy points
}
}
})
.catch((err) => {
console.log("error in pollvoter.js ", err);
this.setState({
error: true,
});
});
}
viewingPoll();
}, [answer]);
useEffect(() => {
let isMounted = true;
fetch("/api/vote", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
poll_id: poll.id,
answer: answer,
}).then(() => {
if (isMounted) {
// location.redirect(`/polls/${result.pollId}`);
location.reload();
}
}),
});
return () => {
isMounted = false;
};
}, [answer]);
I am using React to build a website. I have imported an asynchronous function to execute when I press a button. However, the function is not working asynchronously and I really don't understand why.
interact.js:
export const getNFT = async () => {
setTimeout(() => {
console.log('getNFT code execute');
return nft;
}, 2000);
};
const nft = {
tokenURI: 'https://gateway.pinata.cloud/ipfs/QmdxQFWzBJmtSvrJXp75UNUaoVMDH49g43WsL1YEyb',
imageURL: 'https://gateway.pinata.cloud/ipfs/QmeMTHnqdfpUcRVJBRJ4GQ2XHU2ruVrdJqZhLz',
ID: '212'
};
Main.js
import {
getNFT
} from 'interact.js';
// This function is executed when a user clicks on a button
let getAllocatedNFT = async () => {
try {
let response = await getNFT();
console.log('response from server:: '+response);
}catch(e){
console.log(e);
}
};
console:
response from server:: undefined
getNFT code execute // This is executed correctly after 2 seconds
You have to return promise which will resolve your webAPI(setTimeout)
Please use like below:
const getNFT = async () => {
return new Promise(resolve => setTimeout(() => {
console.log("getNFT code execute")
resolve(true)
}, 2000)
);
};
I have a function handleSubmit that handles registering in Firebase in a react component. Inside, I want to handle errors with my setErrorTimeout function, which has a setTimeout that resets the error automatically after 3 seconds in this case..
The problem is, my Timeout is not executed, e.g the callback function inside the timeout is not being executed after 3 seconds, but everything else is.. why?
const handleSubmit = async e => {
e.preventDefault()
console.log(formDetails)
if (formDetails.password !== formDetails.passwordrepeat) {
setErrorTimeout(setRegisterError, {
message: 'Passwords do not match!',
})
return
}
console.log('Try')
console.log(formDetails.email, formDetails.password)
try {
auth.createUserWithEmailAndPassword(
formDetails.email,
formDetails.password
)
.then(userCredentials => {
if (userCredentials) {
const user = userCredentials.user
let success = user.sendEmailVerification()
console.log('success register:', success)
setRegisterSuccess(
'You registered successfully! please check your email!'
)
setFormDetails({})
}
})
.catch(error => {
console.log('ERROR!')
setErrorTimeout(error)
})
} catch (e) {
setErrorTimeout(e)
}
}
const setErrorTimeout = error => {
console.log('inside timeout!')
setRegisterError(error)
const timer = setTimeout(() => {
console.log('inside cb!')
setRegisterError(null)
}, 3000)
clearTimeout(timer)
console.log('after timeout!')
}
You're clearing the timeout right after you create it here:
const timer = setTimeout(() => {
console.log('inside cb!')
setRegisterError(null)
}, 3000)
clearTimeout(timer)
You probably want that clearTimeout call to be inside the callback, although it's not even strictly needed since the timeout already fired.
I need periodically fetch data and update it to the screen.
I have this code:
const [temperature, setTemperature] = useState();
useEffect(() => {
fetch("urlToWeatherData")
.then(function(response) {
if (response.status !== 200) {
console.log(
"Looks like there was a problem. Status Code: " + response.status
);
return;
response.json().then(function(data) {
console.log(data[0].temperature);
setTemperature(data[0].temperature);
});
})
.catch(function(err) {
console.log("Fetch Error :-S", err);
});
}, [] );
So, is there any neat way to run it every 15 seconds, in example?
Thanks!
Wrap it in an interval, and don't forget to return a teardown function to cancel the interval when the component unmounts:
useEffect(() => {
const id = setInterval(() =>
fetch("urlToWeatherData")
.then(function(response) {
if (response.status !== 200) {
console.log(
"Looks like there was a problem. Status Code: " + response.status
);
return;
response.json().then(function(data) {
console.log(data[0].temperature);
setTemperature(data[0].temperature);
});
})
.catch(function(err) {
console.log("Fetch Error :-S", err);
});
), 15000);
return () => clearInterval(id);
}, []);
Just to give a different approach, you can define a custom hook for extracting this functionality into a reusable function:
const useInterval = (callback, interval, immediate) => {
const ref = useRef();
// keep reference to callback without restarting the interval
useEffect(() => {
ref.current = callback;
}, [callback]);
useEffect(() => {
// when this flag is set, closure is stale
let cancelled = false;
// wrap callback to pass isCancelled getter as an argument
const fn = () => {
ref.current(() => cancelled);
};
// set interval and run immediately if requested
const id = setInterval(fn, interval);
if (immediate) fn();
// define cleanup logic that runs
// when component is unmounting
// or when or interval or immediate have changed
return () => {
cancelled = true;
clearInterval(id);
};
}, [interval, immediate]);
};
Then you can use the hook like this:
const [temperature, setTemperature] = useState();
useInterval(async (isCancelled) => {
try {
const response = await fetch('urlToWeatherData');
// check for cancellation after each await
// to prevent further action on a stale closure
if (isCancelled()) return;
if (response.status !== 200) {
// throw here to handle errors in catch block
throw new Error(response.statusText);
}
const [{ temperature }] = await response.json();
if (isCancelled()) return;
console.log(temperature);
setTemperature(temperature);
} catch (err) {
console.log('Fetch Error:', err);
}
}, 15000, true);
We can prevent the callback from calling setTemperature() if the component is unmounted by checking isCancelled(). For more general use-cases of useInterval() when the callback is dependent on stateful variables, you should prefer useReducer() or at least use the functional update form of useState().