SignalR connect setup in react- using useEffects - reactjs

I'm using "#microsoft/signalr": "^6.0.5", and trying to set up a connection.
It is able to connect with the backend, but I am not sure if my setup looks OK for when the connection fails.
Specifically, I am wondering if the last useEffect is correctly written (the placement of the onClose clause)
useEffect(() => {
const newConnection = new HubConnectionBuilder()
.withUrl(
"https://localhost:3000/workorderHub",
{ accessTokenFactory: () => token, withCredentials: false }
)
.configureLogging(LogLevel.Information)
.withAutomaticReconnect()
.build();
setConnection(newConnection);
}, []);
useEffect(() => {
async function start() {
if (connection) {
try {
connection
.start()
.then(() => {
connection.invoke("SubscribeToProject", projectId); // calling hub method from the client
})
.catch((err) => {
console.error(err.toString());
});
connection.on(
"OperationUpdated",
(projectId, operationId, operation) => {
// function called from the backend Hub
actions.updateSyncedOperation({ operationId, operation });
}
);
} catch (err) {
console.log({ err });
setTimeout(start, 5000);
}
} else {
connection.onclose(async () => {
await start();
});
}
}
start();
}, [connection]);

For React 18 with the new strictMode behaviour i do the following.
It only creates one connection without any errors and it seems to cleanup properly during strictmode behaviour.
export const useLiveUpdates = () => {
const [connectionRef, setConnection] = useState < HubConnection > ();
function createHubConnection() {
const con = new HubConnectionBuilder()
.withUrl(`${EnvService.getUrlHub('url')}`, {
accessTokenFactory: () => AuthService.getToken(),
})
.withAutomaticReconnect()
.build();
setConnection(con);
}
useEffect(() => {
createHubConnection();
}, []);
useEffect(() => {
if (connectionRef) {
try {
connectionRef
.start()
.then(() => {
connectionRef.on('message', () => { ...
});
})
.catch((err) => {
logger.error(`Error: ${err}`);
});
} catch (error) {
logger.error(error as Error);
}
}
return () => {
connectionRef ? .stop();
};
}, [connectionRef]);
};

Related

Possible Unhandled Promise Rejection (id: 0): React Native

I know what caused this error, I explicitly removed the header from axios call to check if the code can handle the error. But my question is I have a catch block in place, but I'm still getting this -> Possible Unhandled Promise Rejection
const getUser1 = () => {
userService.getUser1().then((res) => {
setId(res.data._id);
return Promise.resolve();
}).catch((error) => Promise.reject(error));
};
const getUserComments = () => {
commentsService.getUserComments(‘user1’).then((res) => {
setComments(res.data)
return Promise.resolve();
}).catch((err) => Promise.reject(err));
};
useEffect(() => {
const onInit = async () => {
await Promise.all([
getUser1(),
getUserComments(),
]).catch((ex) => console.log(ex));
};
onInit();
}, []);
Try this version:
const getUser1 = async () => {
try{
const {data} = await userService.getUser1()
setId(data._id);
}
catch(err){
throw new Error(err)
}
};
const getUserComments = async () => {
try{
const {data} = await commentsService.getUserComments(‘user1’)
setComments(data)
}
catch(err){
throw new Error(err)
}
};
const init = useCallback(async () =>{
try{
return await Promise.all([
getUser1(),
getUserComments(),
])
}
catch(err){
console.error(err)
}
}, [])
useEffect(() => {
init();
}, [init]);

How to close connection when page closed and reloaded? (reactjs)

I have a problem with my asp.net core and reactjs app.
I want to fire OnDisconnected method in backend side but I dont know how and when I can call this method?
This is my react code :
useEffect(() => {
const newConnection = new HubConnectionBuilder()
.withUrl('https://localhost:5032/hub/notif',
{
accessTokenFactory: () =>Token
})
.withAutomaticReconnect()
.build();
setConnection(newConnection);
console.log(newConnection);
}, []);
useEffect(async () => {
if (connection) {
connection.start()
.then(result => {
console.log('Connected!');
connection.on('RecieveNotification', message => {
console.log(message);
});
})
.catch(e => console.log('Connection failed: ', e));
}
}, [connection]);

useEffect is not being called when dependency is changed

loadMoreMessages this is being called when the scrollTop gets to 0. and calling setScrollPosition but the useEffect is not being called.
setMessages is working fine
const [scrollPosition, setScrollposition] = useState('')
const { messages, setMessages } = useMessages()
let getMessages = ({onSuccess, onError, finaly = f => f}) => {
socket.emit('get:messages', (err, respo) => {
if(err) {
onError(err)
}
else {
console.log('respo ', respo)
else {
onSuccess(respo)
}
}
})
}
let loadMoreMessages = () => {
getMessages({
onError: err => console.log(err),
onSuccess: data => {
setMessages({
type: 'update',
data
})
console.log('messages updated') // the code dose reach here
setScrollposition('update')
}
})
}
useEffect(() => {
console.log(scrollPosition)
}, [scrollPosition])
might you can do this
useEffect(()=>{
if(scrollPosition === 'something'){
loadMoreMessages();
}
},[scrollPosition])

How to test custom hooks with event listener inside useEffect?

I'm using react native and jest to create my tests. I'm facing problems to test an event listener that listens to url events from expo-linking. This event listenner is inside an useEffect hook.
Below is the code from my custom hook with my useEffect and an event listener inside:
useEffect(() => {
isMounted.current = true;
Linking.addEventListener('url', async () => {
try {
if (!navigation.isFocused() || !isMounted.current) return;
setIsLoading(true);
const response = await api.get('sessions/auth/success');
if (!response.data) return;
console.log('aqui');
const { notRegisteredUser, token } = response.data;
api.defaults.headers.authorization = `Bearer ${token}`;
if (notRegisteredUser && token) {
setIsLoading(false);
navigation.navigate('BirthDateScreen');
dispatch(
updateUser({
...notRegisteredUser,
}),
);
}
} catch (err) {
Alert.alert('Error', `${translate('loginRegisterError')}: `, err);
}
});
return () => {
isMounted.current = false;
};
}, [dispatch, navigation]);
In my test file I have the following mocks:
jest.mock('expo-linking', () => {
return {
addEventListener: (event: string, callback: () => void) => callback(),
};
});
jest.mock('#react-navigation/native', () => {
return {
useNavigation: () => ({
isFocused: mockedNavigationFocus,
navigate: mockedNavigation,
}),
};
});
jest.mock('react-redux', () => {
return {
useDispatch: jest.fn(),
};
});
jest.mock('../../../store/modules/user/actions', () => {
return {
updateUser: jest.fn(),
};
});
jest.mock('i18n-js', () => {
return {
locale: 'en',
t: () => 'any key',
};
});
Finally this is how my test looks in my first try:
it('should pass the test', async done => {
mockedNavigationFocus.mockImplementation(() => true);
apiMock.onGet('sessions/auth/success').reply(200, {
notRegisteredUser: { name: 'Logan' },
token: '123',
});
render(<LoginScreen />);
await waitFor(() =>
expect(mockedNavigation).toHaveBeenCalledWith('BirthDateScreen'),
);
done();
});
In my second try this is how my test looked (I used renderHooks from #testing-library/react-hooks):
it('should pass the test', async () => {
mockedNavigationFocus.mockImplementation(() => true);
apiMock.onGet('sessions/auth/success').reply(200, {
notRegisteredUser: { name: 'Logan' },
token: '123',
});
const { result, waitForValueToChange } = renderHook(() => useLoginButton());
const { isLoading } = result.current;
await waitForValueToChange(() => isLoading);
await waitForValueToChange(() => isLoading);
expect(mockedNavigation).toHaveBeenCalledWith('BirthDateScreen');
});
With both tests I get the following error:
test error
Another error I get is that my callback function inside useEffect runs many times before it stops and this does not happen when I am not testing.
Does anyone knows how can I write this test?

useState not triggers rerendering in websocket callback handler

Using web socket(#aspnet/signalr) it works fine(in component callback is receiving the message)fine, I am able to receive and trigger callback in component(connection.on("UpdateProgress"... ) inside this callback its increment counter which is state variable(numberOfFailed).. it triggers rendering only once, I set debugger and see numberOfFailed is always 0.
What's wrong here? why calling setNumberOfFailed doesn't change the value of numberOfFailed.
here is the code;
const [numberOfFailed, setNumberOfFailed] = useState(0);
const [connection, setConnection] = useState(null);
useEffect(() => {
const newConnection = new HubConnectionBuilder()
.withUrl(`${config.API_BASE_URL}update-progress`, {
transport: HttpTransportType.WebSockets,
accessTokenFactory: () => {
return `${localStorage.token}`;
},
})
.build();
setConnection(newConnection);
}, []);
useEffect(() => {
const fetchData = async () => {
if (connection) {
try {
await connection.start();
connection.onclose((error) => {
console.info('Connection Closed:', error);
});
if (connection.state === HubConnectionState.Connected) {
connection.on('UpdateProgress', (message) => {
debugger;
if (message.count) {
setTitleText(`Bildirim Gonderim Başladı, Toplam Alıcı Sayısı:${message.count}`);
} else if (message.status == 1) {
let _t = numberOfFailed + 1;
setNumberOfFailed(_t);
}
console.info('message', message);
});
}
} catch (err) {
console.log(err);
}
}
};
fetchData();
}, [connection]);
It was because react not trace the updated of variables which not explicitly defined in DependencyList. The best solution for this change the way..
This is how I solve this problem;
The main idea is using useReducer hook to update variables and use them in render.
const [connection, setConnection] = useState(null);
const [counts, dispatch] = useReducer(BaskentMobilReducer, INITIAL_VALUE);
useEffect(() => {
const newConnection = new HubConnectionBuilder()
.withUrl(`${config.API_BASE_URL}update-progress`, {
transport: HttpTransportType.WebSockets,
accessTokenFactory: () => {
return `${localStorage.token}`;
},
})
.build();
setConnection(newConnection);
}, []);
useEffect(() => {
const fetchData = async () => {
if (connection) {
try {
await connection.start();
connection.onclose((error) => {
console.info("Connection Closed:", error);
});
if (connection.state === HubConnectionState.Connected) {
connection.on("UpdateProgress", (message) => {
if (message.count) {
setTotalCount(message.count);
setTitleText(
`Bildirim Gonderim Başladı, Toplam Alıcı Sayısı:${message.count}`
);
} else if (message.status == 0) {
debugger;
dispatch({
type: "UPDATE_COUNTS_SUCCESS",
});
console.log("counts", counts);
} else if (message.status == 1) {
debugger;
dispatch({
type: "UPDATE_COUNTS_FAIL",
});
console.log("counts", counts);
}
console.info("message", message);
});
}
} catch (err) {
console.log(err);
}
}
};
fetchData();
}, [connection]);

Resources