How to handle Errors and Exceptions in Redux Reducers? - redux

We are currently testing our Error Handling in our React / Redux App. (I already read https://github.com/reduxjs/redux/issues/1960). In React we implemented ErrorBoundaries and this is working pretty well: No more empty pages for unhandled exceptions.
No we tried throwing Errors in Reducers and we are seeing them logged in the console with an untouched redux state.
Reducers MUST BE pure!
I can definitely support this. The function should be "easy" with no side effects and well tested. The thing is: the reducer developer can make false assumptions (e.g. about which property in the redux state can be undefined). The example results in an
[Error] Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating 'someVariableWithCanBeNullOrUndefined.someFunction')
One can argue: at least no white page. But the state of the App is kind of awkward now since the actions got all dispatched and executed but were not correctly reduced. These kind of errors can easily be overseen.
Is there a way to handle these programming runtime errors? Maybe even present them in React's ErrorBoundary?
An example of an erroneous reducer function which results ONLY in a log entry:
reduceSomeState(state = new SomeState(), action: SomeAction) {
throw new Error('some runtime error: e.g. a value in the state is undefined and accessed here');
}

To my knowledge exceptions can not be handled in reducers nor connect(mapStateToProps, mapDispatchToProps)(View).
Though a solution is to set a global error handler. One can look like this:
import * as React from 'react';
import {render} from 'react-dom';
import {GlobalErrorView} from '../../globalError/GlobalErrorView';
window.onerror = (event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error) => {
render(
(
<GlobalErrorView
error={error}
okAction={() => window.location.reload()}
/>
),
document.getElementById('app') as HTMLElement
);
};

Related

Nuxt 3 can not get the URL hash inside middleware

I am trying to get the hash part from the URL but for some reason at the first load of the page, the middleware will not see the hash.
This is not the case with a reload or when using the navigateTo() method
export default defineNuxtRouteMiddleware(async (to) => {
console.log("Auth -- hash:", to.hash)
})
Got some answers from GitHub, check the conversation:
https://github.com/nuxt/framework/discussions/7505#discussioncomment-3644705
Solution
For getting the hash I just removed the middleware, since is impossible to get the browser hash values on the server. Instead, I changed to using a composable function and calling it from the page that received the URL inside an onMounted() hook.
<script setup lang="ts">
const route = useRoute();
onMounted(() => {
oauthCallback(route.hash) // composable function
})
</script>

Post request redux thunk

I have GET requests and normally when those succeeded I save data in store, but for POST requests I need to know if it succeeded or not, in order to execute some code (show a message and redirect), the docu says you can use an isLoading variable, but it just says if the service is working but not if it succeeded, if I try to create a new success variable in the store, it will be turned on forever after the request and I don't need that either. I tried returning a promise from the action creator and handle response directly inside the component but it looks like the same to call axios there instead of using redux.
My action creator looks like this:
export function createProject(userId, projectName) {
return function (dispatch) {
dispatch({ type: projectsActions.START_CREATE_PROJECT });
return ProjectsService.createProject(userId, projectName).then(() => {
dispatch({ type: projectsActions.SUCCESS_CREATE_PROJECT });
}).catch((error) => {
dispatch({ type: projectsActions.ERROR_CREATE_PROJECT });
throw error;
});
}
}
I understand where your doubts are coming from, it doesn't seem appropriate to have a field on your Redux store only to know the success of a one-time request.
If you only need to make a post request and only care about it's result once, the simplest way to do it is to use state in the component making the request. Component-level state is easily manageable and gets removed from memory when the component is unmounted, but on the other hand you may want to have a single source of truth for your app. You have to make a choice, but your Redux implementation is correct.

redux-saga and firebase - Can't log the user out in a clean way

As a preface, let me mention that I have never used redux-saga or Firebase before today. I'm currently playing around to get a feel for both technologies.
I'm probably just missing a small concept, but I can't seem to get signout to work on my app. I figured that I should probably use call() to manage side effects within a saga, but nothing does the trick.
Here's the saga in question:
export function* deauthenticateUser() {
yield takeLatest(DEAUTHENTICATE_REQUEST, function* () {
try {
yield call(firebase.auth().signOut)
yield put({ type: DEAUTHENTICATE })
}
catch (error) {
yield put({
type: DEAUTHENTICATE_FAILURE,
payload: error,
error: true,
})
}
})
}
I confirmed that calling firebase.auth().signout() directly works, it's only when using call() that I get the error action. Note that there's also no payload when the error gets dispatched.
I checked in Firebase's documentation, and apparently firebase.auth().signout() returns a promise with nothing as it's content. I'm starting to wonder if that wouldn't be the problem, maybe redux-saga does not like having no result in it's promise when using call()?
How would one handle authentication and especially logging out with Firebase and redux-saga?
From a comment from NULL SWEΔT, I had to call yield call([firebase.auth(), firebase.auth().signOut]).
The reason for this is because of JS' context and how this works. More details by reading this and this (read documentation for call([context, fn], ...args)).

AngularFire httpsCallable Object(...) is not a function

I want to call a httpsCallable function in my Ionic 3 app. I am attempting to follow these docs: https://firebase.google.com/docs/functions/callable
I tried:
const newGame = (firebase as any).functions().httpsCallable('findCreateGame');
newGame({}).then(function(result) {
console.log("In here.");
});
Which resulted in:
ERROR TypeError: WEBPACK_IMPORTED_MODULE_5_firebase_functions.functions is not a function
I also tried the newly implemented wrapper in angularfire:
const newGame = this.afFunctions.httpsCallable('findCreateGame');
newGame({}).then(function(result) {
console.log("In here.");
});
ERROR TypeError: Object(...) is not a function
Does anyone have any experience with this yet? Here is the pull request to add the functionality if that helps at all. https://github.com/angular/angularfire2/pull/1532
EDIT---
This code actually calls the Cloud function even though it throws the same 'Not a function' error:
const newGame = this.afFunctions.httpsCallable('findCreateGame');
newGame();
I'm not sure how else to call the function, even though newGame is an object and not a function reference.
The Object(...) is not a function is thrown because you're running rxjs 5, rather than 6.
If you upgrade, the function will perform as expected.
See the rxjs migration doc for more details on the changes between 5 and 6.
In your first example make sure you are importing import 'firebase/functions' in the ts file you're calling the function.

How to access custom response values from a page script?

This might sound like a silly question but I have really tried everything I could to figure it out. I am creating a variable and adding it to my response object in custom Express server file like so:
server.get('*', (req, res) => {
res.locals.user = req.user || null;
handle(req, res);
});
Now I want to access this res.locals.user object from all of my pages, i.e. index.js, about.js, etc., in order to keep a tab on the active session's user credentials. It's got to be possible some way, right?
P.S.: Reading some thread on the NextJS Github page, I tried accessing it from my props object as this.props.user but it keeps returning null even when a server-side console.log shows non-null values.
The res object is available on the server as a parameter to getInitialProps. So, with the server code you have above, you can do
static async getInitialProps({res}) {
return { user: res.locals.user }
}
to make it available as this.props.user.

Resources