How Can I Test props.onChange in React? - javascript

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

React-native AsyncStorage Issue

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('');
}
});

passing 2 functions to useEffect and getting an error "Can't perform a React state update on an unmounted component."

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]);

Trying to execute an imported Async function but the function is not behaving asynchronously

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)
);
};

React.js and Firebase auth: setTimeout Callback function not executed?

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.

React hooks. Periodic run useEffect

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().

Categories