Suppose I am writing an application in Redux and I am tasked to add logging using a 3rd party library. Its API is as follows:
function createLogger(token) {
// the logger has internal state!
let logCount = 0;
return {
log(payload) {
logCount++; // modify local state
fetch('/someapi', { // ship payload to some API
method: 'POST',
body: payload
});
}
};
}
I would then use the library something like this:
let logger = createLogger('xyz');
logger.log('foobar');
I definitely want to create the logger instance just once during application init. But then the question is: where do I store the logger instance?
First instict is to put it somewhere in the store. But is that a good idea? As I have demonstrated in the code the logger object is stateful, it stores a counter in the closure. I do not get a new instance like I would with an immutable object. As we know, state should only be modified via pure reducer functions.
Other possibilities are to create the instance somewhere in a redux middleware closure or just create a global variable, which is obviously evil in terms of testability.
Is there a best practice for this (I would think) rather common scenario?
The state in Redux is stored in memory, in the Redux store. This means that, if you refresh the page, that state gets wiped out. The state in redux is just a variable that persists in memory because it is referenced (via closure) by all redux functions.
The Redux store is created using the configureStore function from Redux Toolkit. configureStore requires that we pass in a reducer argument. Our application might be made up of many different features, and each of those features might have its own reducer function.
It's simple to get access to the store inside a React component – no need to pass the store as a prop or import it, just use the connect function from React Redux, and supply a mapStateToProps function that pulls out the data you need. Then, inside the component, you can pass that data to a function that needs it.
Since you are using ES6 modules I would setup your logger as a module, export
it, and import
it wherever you plan to use it. I think logging from the actions is a solid plan, since it keeps the components unaware, and doesn't pollute the store with side-effects.
function createLogger(token) {
// the logger has internal state!
let logCount = 0;
return {
log(payload) {
logCount++; // modify local state
fetch('/someapi', { // ship payload to some API
method: 'POST',
body: payload
});
}
};
}
export default const logger = createLogger('xyz');
Your action creators
import logger from 'logger-module';
//
logger.log('somestuff');
Testing is still easily achievable by importing the logger and placing whatever spy/stub on its methods that you need to intercept.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With