Javascript ES6 reuse exports with callback - javascript

Let's say I have 2 JavaScript files like the followings and they both import from fileimport.js(reusability)
The goal is, the fileimport.js has an event listener runs on each page. I want to call custom functions for each of the pages after the event runs.
file1.js
import * as fileimport from './fileimport';
...
callback(){
//run custom function
}
file2.js
import * as fileimport from './fileimport';
...
callback(){
//run custom function
}
fileimport.js
...
export ReusableFunc1(){
....
}
export ReusableFunc2(){
....
}
export Func3{
form.addEventListener('submit', function (e) { callback })// I want to call callback() for each of the pages that import this.
}
I tried adding the callback() inside the fileimport.js and override in the regular pages but it did not work out(it did not call the regular pages function). How can I achieve something like this.

If you have a file A.js which provides code, that is used in B.js and C.js, than this file loaded and parsed only once. So a function call within A.js will be triggered only once, when the file is loaded.
If you want to call a function, provided by A.js whenever code from A.js is included somewhere, you cannot use an event listener, since there isn't any.
One thing you always can do is to trigger the desired function »manually« whenever you import A.js.
//A.js
const fx1 = () => {}
const fx2 = () => {}
const callback = () => {}
export { callback }
export default { fx1, fx1 }
//B.js
import { * as CodeFromA, callback } from 'A.js';
callback();
Another thing could be to change the export of A.js to something like:
export default () => {
callback();
return { ReusableFunc1, ReusableFunc2 }
}
This way you can:
import CodeFromA from 'A.js'
const ReusableFx = CodeFromA();
That would ensure that you get error if you try to use any of those ReusableFunc* without triggering the callback before.

The problem is callback is not defined in fileimport.js. You would need circular dependency in order to achieve that, but I suggest you treat them with care, since, quoting this relevant article which I suggest you read:
They are not always evil, but you might want to treat them with special care. They cause tight coupling of the mutually dependent modules. These kinds of modules are harder to understand and reuse as doing so might cause a ripple effect where a local change to one module has global effects.
If you don't need to call the callback function at import time, you shouldn't give you many problems though.
That being said, this would be the code to achieve what you want with a function:
file1.js
import * as fileimport from './fileimport';
...
export function callback() {
//run custom function
}
fileimport.js
import * as file1 from './file1';
ReusableFunc1(){
....
}
eventListener() {
file1.callback();
}
As you can see, the problem remains if you have a lot of callback functions. If the number of functions you need to call increases, I suggest you change your code architecture not to have so much dependency from other modules.

Related

Testing a file that calls its own function

I have a file that is the entry-point to my application;
const main = async () => {
// Do stuff
}
main().then();
I understand that I can export the main function, delete the call in the first file and in a separate file import and call the function there;
const entry = require('./rewritten-entry.js');
entry.main().then();
But for arguments sake lets say I really want to test this first file's main function. As soon as I import the file in my test, the main function will execute before I even reach the test code;
const entry = require('./original-entry.js') // <-- main function executes here
const test = {
// Do test stuff
}
For a test, I don't want this to happen. Likewise I don't want to mock the function since it is exactly what I want to test.
I see this common 'init' logic all over the place so I imagine there is a way to test this.

How to test an internal function

I'm trying to create a unit test to cover an internal function
fileA.js
module.exports.functionA = () {
const functionB = () {
// do something
}
functionB()
}
test.js
const { functionA } = require('fileA')
...
it('runs functionB', () => {
functionA()
expect(...).toHaveBeenCalled()
}
How do I access it?
There are two possibilities here (your situation looks like the first one, but is also clearly simplified for the purposes of the question).
Either:
functionB is entirely private to functionA, part of its implementation, which means you can't access it to test it (directly). Instead, test functionA, which presumably uses functionB as part of the work it does (otherwise, there's no point in an entirely private function in functionA).
or
functionA exposes functionB in some way (for instance, as a return value, or as a method on a returned object, or by setting it on an object that's passed in, etc.), in which case you can get it in whatever way functionA provides and then test it.
(Again, I think yours is the first case.)

how to make a custom hook being executed once globally

I have a hook that adds a header tag in order to load a script, but in case two components are using it, the hook could fall into the case that the header is added twice.
There are several ways to avoid this. Two of them could be:
Use a provider, not a hook that several components could call. Actually I don't want this, I want a hook because at most two components will use it, and in some cases I don't even need it;
The hook calls a function called const addHeaderTag = () => {...}. I could add a property to it and check if the function is called just once, otherwise return silently. It could be safe because the function is defined by me and I control the function object, plus javascript is monothread and concurrency is out of scope;
I could add an id to the header so as to check if it's on the DOM already. I'd avoid it in order to not access the DOM too much
Do you see other better ways? Do you see any problem to the solutions I had in mind?
A solution for this would be using a variable outside of your custom hook to check whether or not your hook is already called
import { useEffect } from "react";
let isCalled = false;
export const useOnce = () => {
useEffect(() => {
if (!isCalled) {
// do this only once, call your function here
console.log("hey there");
isCalled = true;
}
}, []);
return isCalled;
};
The reason this works is because when you import the same module multiple times, the code in that module is still only evaluated once.
That means isCalled in this case is only initialized once, so we can depend on it to check/set the value accordingly for the entire app.
Live example

ES6 Modules, stop execution of code

It sounds like an odd question: How can I leave the execution context from an ES6 module?
See the following example:
Module1 requires some features in the browser to be present (say some new stuff not every Browser implements currentl).
You want to do a check in the module, if the required feature is available and if not, you want to stop the execution.
When writing that with an immediate function call, you can just call return and the following code is never executed.
How to do that in an ES6 module? Do I have to wrap the whole code into an if block?
If you export executable code from an ES6 module, you usually do so as a function:
export function myFunction() {
...
}
---
import { myFunction } from './module';
myFunction();
This function works just like any other that you define directly in the calling module. You can simply return to the caller as you would otherwise:
export function myFunction() {
if(...) return;
...
}
There's no special tools (that I'm aware of) in ES6/ES2015 to do this. Furthermore, all exports must be top-level. This also means that if a module ever has a given export, it will always have that export. The value of that export can change, of course, but the export "interface" must always be the same.
If you don't want to export certain values if a condition isn't met then you won't be able to do that. However, you can always make those values null or undefined in that case. If exporting isn't a problem, there are a few tools from ES5 and before that you can use.
The IIFE (as you know):
(function() {
...
if (someCondition) {
return;
}
// code that won't run
})();
A single iteration for loop (no idea why you'd use this but you could):
for (var i = 0; i < 1; i++) {
...
if (someCondition) {
break;
}
// code that won't run
}
And a good old-fashioned if statement:
...
if (!someCondition) {
// code that won't run
}

wait for an async operation before requiring other modules

In my main.js, I am reading a file asynchronously. Once my file is loaded, I set some objects in GLOBAL namespace and use them in my required modules (using GLOBAL namespace or not is a different story, I am using it anyway).
My required module immediately expects that variable to exist at the time of loading. So how do I make it wait till my file reading is complete in the main.js? Do I simply require module in the callback of readFile? Or there's a better way to do it?
example:
fs.readFile('./file', function (data) {
// do something
GLOBAL.obj = data;
});
require('./module.js');
module.js
obj.someFunction();
Your gut feeling of disliking that solution is understandable. (Your stomach is right). The proper way of cleaning this up (and you should take the time – future-you will thank you for it):
go through every one of your ten modules
for each one go through all the functions it exports
for each function figure out, what globals they actually depend on.
add those as arguments to the function.
if they take a lot of arguments now, consider grouping them into objects, creating useful models.
if a bunch of functions all depend on the same set of variables, you can also consider creating a factory function
create a function that takes the formerly global variables as arguments and wrap all of the module's code into that function.
make that function the single export of your module. It serves as a factory function and creates the context for all the other functions in that module. It should return whatever the module exported before.
Example
// DB used to be a global
module.exports = function(DB) {
function getUser(user, cb) {
DB.get('user', db);
}
return {getUser: getUser};
};
You can then use it like this:
var module = require('module')(DB);
module.getUser(myUser, function(){}};
Yes, just follow the rule #1 of async programming. Stuff that depends on callback happening must be executed in that callback. Since your require depends on the variable set in async, you can only use your require inside it:
fs.readFile('./file', function (data) {
// do something
GLOBAL.obj = data;
require('./module.js');
});

Categories