How can I run a function when internet connection is established? - reactjs

The app is a quiz, and if user finishes the round he may send the points in firebase. If user is not connected to internet, I save the points in device memory, so when connection is established the points are send in firebase.
The best would be to let this happen automatically and show a message...
I'm trying to do this in App.js in a useEffect, but it checks only if I refresh the app. I tried withNavigationFocus and useFocusEffect but error: the App.js is unable to get access to navigation....
I could also move the code to WelcomeScreen.js and show a button if connection is established to add the points, but it's not that user friendly.
Any ideas would be appreciated.
Thanks!
useEffect(() => {
const getPoints = async () => {
let points = await AsyncStorage.getItem("savedPoints");
if (!!points) {
const getEmail = async () => {
const userData = await AsyncStorage.getItem("userData");
if (userData) {
const transformedData = JSON.parse(userData);
const { userEmail } = transformedData;
return userEmail;
}
};
const email = await getEmail();
// Give it some time to get the token and userId,
// because saveData needs them.
setTimeout(
async () => await dispatch(dataActions.saveData(email, +points)),
3000
);
await AsyncStorage.removeItem("savedPoints");
}
};
NetInfo.fetch().then(state => {
if (state.isConnected) {
console.log("isConnected");
getPoints();
}
});
}, []);
The solution
WelcomeScreen.js
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
console.log("useEffect welcome");
const unsub = NetInfo.addEventListener(state => {
setIsConnected(state.isConnected);
});
return () => unsub();
}, []);
const getPoints = async () => {
console.log("getPoints welcome");
let points = await AsyncStorage.getItem("savedPoints");
if (!!points) {
const getEmail = async () => {
const userData = await AsyncStorage.getItem("userData");
if (userData) {
// parse converts a string to an object or array
const transformedData = JSON.parse(userData);
const { userEmail } = transformedData;
return userEmail;
}
};
const email = await getEmail();
// Give it some time to get the token and userId,
// because saveData needs them.
setTimeout(
async () => await dispatch(dataActions.saveData(email, +points)),
3000
);
await AsyncStorage.removeItem("savedPoints");
}
};
if (isConnected) getPoints();

You can set up a listener to listen for an internet connection. Don't use any logic in app.js, use it in a separate screen component.
constructor(props) {
super(props);
this.state = {
isConnected: false
};
}
componentDidMount() {
this.listenForInternetConnection = NetInfo.addEventListener(state => {
// your logic is here or setState
this.setState({
isConnected: state.isConnected
});
});
}
componentWillUnmount() {
this.listenForInternetConnection();
}

You can use JS EventListeners
window.addEventListener('load', () => {
navigator.onLine ? showStatus(true) : showStatus(false);
window.addEventListener('online', () => {
showStatus(true);
});
window.addEventListener('offline', () => {
showStatus(false);
});
});

Related

Unit testing onSuccess for a react query call

I have a component that looks like this:
<TestComponent refetch={fn} />
Within TestComponent, I have a save button that fires off a mutation:
const handleSaveClick = () => {
const reqBody: AddHotelRoomDescription = {...}
addHotelRoomDescriptionMutation(reqBody, {
onSuccess: () => {
closeDrawer();
handleRefetch();
}
})
}
Within my test file for this component, I am trying to ensure handleRefetch gets called:
it('successfully submits and calls the onSuccess method', async () => {
const mockId = '1234'
nock(API_URL)
.post(`/admin/${mockId}`)
.reply(200);
const user = userEvent.setup();
const editorEl = screen.getByTestId('editor-input');
await act( async () => { await user.type(editorEl, 'Updating description'); });
expect(await screen.findByText(/Updating description/)).toBeInTheDocument();
const saveButton = screen.getByText('SAVE');
await act( async () => { await user.click(saveButton) });
expect(mockedMutate).toBeCalledTimes(1);
await waitFor(() => {
expect(mockedHandleRefetch).toBeCalledTimes(1); <-- fails here
})
})
I am not sure how to proceed here. I know I can test useQuery calls by doing:
const { result } = renderHook(() => useAddHotelRoomDescription(), { wrapper }
But I think this is for a different situation.
Appreciate the guidance!

Take photo and video with same component react native

My goal is to be able to take a photo when tapped, and a video when pressed down and held.
Currently, my code takes a photo. But when I try trigger a recording event with onLongPress, I can't seem to get it to work.
Any ideas what I'm doing wrong?
const onLongPressButton = () => {
setLongPressed(true);
};
useEffect(() => {
if (longPressed == true) {
const startRecord = async () => {
console.log("RECORDING");
if (cameraRef.current) {
setRecording(true);
const recordedVideo = await cameraRef.current.recordAsync();
setVideo(recordedVideo);
setLongPressed(true);
}
};
startRecord();
}
}, [longPressed]);
const stopRecord = async () => {
setLongPressed(false);
console.log("STOP RECORDING");
setRecording(false);
await cameraRef.current.stopRecording();
};
const handlePhoto = async () => {
console.log("Photo");
if (cameraRef.current) {
let photo = await cameraRef.current.takePictureAsync({});
console.log(photo.uri);
}
};
And here is my component:
<Circle
onPress={handlePhoto}
onLongPress={onLongPressButton}
onPressOut={stopRecord}
delayLongPress={200}
/>

React Hook useEffect has a missing dependency: 'fetchUser'. useEffect problem?

I'm new to react and I'm learning how to use useEffect. I encountered this warning in my react app. I tried out some solutions on SO but the warning still remains. Both fetchUser and fetchPosts trigger this warning. Can anyone enlighten me what is the problem and what does the warning mean?
App.js
useEffect(() => {
setLoading(true)
const getUser = async () => {
const userFromServer = await fetchUser()
if (userFromServer) {
setUser(userFromServer)
setLoading(false)
} else {
console.log("error")
}
}
getUser()
}, [userId])
useEffect(() => {
const getPosts = async () => {
const postsFromServer = await fetchPosts()
setPosts(postsFromServer)
}
getPosts()
}, [userId])
useEffect(() => {
const getUserList = async () => {
const userListFromServer = await fetchUserList()
setUserList(userListFromServer)
}
getUserList()
}, [])
// Fetch user
const fetchUser = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
const data = await res.json()
return data
}
// Fetch posts
const fetchPosts = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
const data = await res.json()
return data
}
// Fetch list of users
const fetchUserList = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/')
const data = await res.json()
return data
}
If you are using any function or state which has been declared outside the useEffect then you need to pass it in the dependency array like this:
const someFunctionA = () => {
....
}
const someFunctionB = () => {
....
}
useEffect(() => {
....
}, [someFunctionA, someFunctionB])
You can read more about it here in case you want to know how it will be rendered: React useEffect - passing a function in the dependency array

react hooks - state lose value in function

I've a problem, I use the react hooks to keep track of the user token and I use the token to identify that user in a socket connection.
The problem is that when I mount the component and set the socket the token works as expected, after when the app goes in background I close the socket but when the app come in foreground and I executed the setToken function to re-set the connection the token in the function have his starter value (false). I print the token on screen and also when in the function appear to be false in the screen is printed correctly.
Here my code:
let socket;
const Chat = (props) => {
const [messages, setMessages] = useState([]);
const [users, setUsers] = useState({});
const [token, setToken] = useState(false);
useEffect(() => {
init();
return ()=> {
socket.close();
AppState.removeEventListener('change', appStateChange);
}
}, []);
const init = async () => {
// [...] get the token
};
const appStateChange = async (newState) => {
if (newState === "active") {
setSocket(); //--------- EXECUTING FROM HERE THE TOKEN IS FALSE ---------//
}
if (newState !== "active") {
socket.close();
}
}
useEffect(() => {
if (token) {
setSocket(); //--------- EXECUTING FROM HERE THE TOKEN IS CORRECT ---------//
}
}, [token]);
const setSocket = async () => {
socket = io("http://192.168.1.172:3000/", {
query: {
token: token,
userTo: props.userTo
},
});
socket.on("init", (data) => {
setUsers(data.users);
setMessages(data.messages);
});
socket.on("newMessage", (data) => {
onReceive({
_id: data._id,
text: data.text,
createdAt: new Date(),
user: {
_id: data.user._id,
name: data.user.name,
avatar: data.user.avatar,
},
});
});
};
const onSend = useCallback((messages = []) => {
setMessages((previousMessages) =>
GiftedChat.append(previousMessages, messages)
);
socket.emit("newMessage", messages);
console.log(messages)
}, []);
const onReceive = useCallback((received) => {
setMessages((previousMessages) =>
GiftedChat.append(previousMessages, received)
);
}, []);
return (
<View style={{flex:1}}>
<Text>{token}</Text>
{/*--------- HERE THE TOKEN IS CORRECT ---------*/}
</View>
);
};
export default Chat;
Some State setting you are missing out!
When the app goes to background, you didn't cleared the token.
For appStateChange function parameter newState, from where you are intializing the value
For newState when the app comes to front again, you didn't changed the value so the setSocket() will not be called

Separate functions which depend on each other

I am trying to clean up my code an separate into functions that only have one task.
In v1 joinDailyCo(url); was defined inside fetchUrl(). Now I tried to move it out with
const url = fetchUrl();
joinDailyCo(url);
However, as soon I do that, I get the error message:
Unhandled Rejection (TypeError): Cannot read property 'join' of
undefined
const Daily = ({ eventSlug, tableId }) => {
const classes = useStyles();
const iframeRef = useRef();
const dailyRef = useRef();
const joinedRef = useRef();
useEffect(() => {
// Join call
const joinDailyCo = async (url) => {
if (joinedRef.current) {
// This is needed due to it never returning if there wasn't a meeting joined first.
await dailyRef.current.leave();
}
await dailyRef.current.join({ url });
};
// Retrieve dailySessionId and meetingToken.
const fetchUrl = async () => {
try {
const {
data: { dailySessionId, meetingToken },
} = await api.get(
`events/${eventSlug}/space/tables/${tableId}/daily-auth/`
);
const url = `${DAILY_URL}/${dailySessionId}?t=${meetingToken}`;
return url;
// joinDailyCo(url);
} catch (error) {
Sentry.captureException(error);
}
};
const url = fetchUrl();
url && joinDailyCo(url);
}, [eventSlug, tableId]);
useEffect(() => {
dailyRef.current = DailyIframe.wrap(iframeRef.current, {
// showLeaveButton: true,
});
dailyRef.current.on(eventTypes.LEFT_MEETING, () => {
joinedRef.current = false;
});
dailyRef.current.on(eventTypes.JONING_MEETING, () => {
joinedRef.current = true;
});
return () => {
dailyRef.current.destroy();
};
}, []);
return (
<iframe
ref={iframeRef}
className={classes.root}
title="Video Meeting"
allow="camera; microphone; display-capture; fullscreen"
/>
);
};
export default Daily;

Resources