For a 'back' button I've created below (onClick) handler function in my React app.
const { length: historyLength, goBack, replace } = useHistory();
const handleBack = () => {
if (historyLength > 2) {
goBack();
} else {
// History length is 2 by default when nothing is pushed to history yet
// https://stackoverflow.com/questions/9564041/why-history-length-is-2-for-the-first-page
replace(HomePage);
}
};
Then I am passing the onClick handler to my child component like: <Button onClick={handleBack}/>
I am using this handleBack function in multiple places in my React app. Is it a good approach make it e.g. a helper function and how exactly?
I also don't see any issue with the code or using it as a utility callback.
Is it a good approach make it e.g. a helper function and how exactly?
Anytime you can make your code more DRY (Don't Repeat Yourself) it's generally a good thing. My personal rule-of-thumb is if I've written the same utility code a third time I'll spend a bit of time to refactor it into a common utility (and unit test!!).
I might suggest creating a custom hook to return the back handler.
Example:
import { useHistory } from 'react-router-dom';
const useBackHandler = () => {
const history = useHistory();
const handleBack = React.useCallback(() => {
const { length: historyLength, goBack, replace } = history;
if (historyLength > 2) {
goBack();
} else {
replace(HomePage);
}
}, []);
return handleBack;
};
export default useBackHandler;
Now you have a single hook to import and use.
import useBackHandler from '../path/to/useBackHandler';
...
const backHandler = useBackHandler();
...
<button type="button" onClick={backHandler}>Back?</button>
If you are needing this function in older class components, then you'll need a way to inject the handleBack as a prop. For this you can create a Higher Order Component.
Example:
import useBackHandler from '../path/to/useBackHandler';
const withBackHandler = Component => props => {
const backHandler = useBackHandler();
return <Component {...props} backHandler={backHandler} />;
};
export default withBackHandler;
To use, import withBackHandler and decorate a React component and access props.backHandler.
import withBackHandler from '../path/to/withBackHandler';
class MyComponent extends React.Component {
...
someFunction = () => {
...
this.props.backHandler();
}
...
}
export default withBackHandler(MyComponent);
#meez
Don't see why this wouldn't work. Just a couple of things: (a) I would add the event argument and e.preventDefault() within the function and (b) would be careful of the function name you are passing on the onClick property of your button: handleBackClick !== handleBack, you'll get an ReferenceError because of an undefined function.
Additionally, I also noticed that this can be achieved with native browser functions. Here's a snippet:
const { length: historyLength, back } = window.history;
const { replace } = window.location;
const handleBack = (e) => {
e.preventDefault();
if (historyLength > 2) {
back();
} else {
replace('homepageUrl');
}
};
Hi so I have two files and i want to convert one of them into a react functional component. The first one is PuzzleGrid and it will call the function Grid.getMatrix()
export const PuzzleGrid = () => {
const newMatrix = GridLib.getMatrix();
}
and the next is Gridlib which is a react class :
export class GridLib {
static getMatrix() {
return Array(6).fill(Array(6).fill(0));
}
}
I want to be able to convert the class into a function, this is what i have, however i am getting an error. does anyone understand why?
export const GridLib = () => {
GridLib.getMatrix = {
Array(6).fill(Array(6).fill(0));
}
}
You can simply return the results of the getMatrix method:
export const GridLib = () => {
return Array(6).fill(Array(6).fill(0));
}
However, this will cause a problem for your PuzzleGrid component, as it depends on GridLib having a getMatrix method. You would have to simply have PuzzleGrid return a GridLib for it to work as such:
export const PuzzleGrid = () => {
return <GridLib />
}
However this pattern is a bit silly and repetetive. I feel like there must be more going on to your question, but you may have reduced it down too much and stripped away some important details. What is the larger context of this issue?
Just use a function properties to declare a "static method"
export const GridLib = () => {
return <View/> ;
}
GridLib.getMatrix = () => {
return Array(6).fill(Array(6).fill(0));
}
I needed to create a custom hook which is supposed to contain all the handlers that will be used everywhere in my page. My requirements were;
Handlers are supposed to be accessible from all the components in the page
Handlers should be able to use other hooks, like useContext
So, created a useHandlers hook sandbox
However, couldn't make the LogHandler accessible from the page, receving LogHandler is not a function
Any idea?
The issue why you're getting LogHandler is not a function is because it's undefined and it doesn't get initialized until HandlerComp gets called:
export const userHandlers = (): IUseHandlers => {
// initialization skipped, so `LogHandler` is undefined
let LogHandler: () => void;
const HandlersComp: React.FunctionComponent<HandlersProps> = (
props: HandlersProps
) => {
// initialized here, but that's after `HandlersComp` gets caled
LogHandler = () => {
console.log("Hi from LogHandler");
};
return <></>;
};
return { HandlersComp, LogHandler };
}
I suggest you move the initialization step from HandlersComp like so:
export const useHandlers = (): IUseHandlers => {
const LogHandler: () => void = () => {
console.log("Hi from LogHandler");
};
const HandlersComp: React.FunctionComponent<HandlersProps> = (
props: HandlersProps
) => {
LogHandler()
return <></>;
};
return { HandlersComp, LogHandler };
};
Some notes:
HandlersComp looks like it should be a separate and reusable component, rather than a hook
LogHandler also looks more like a utility function, rather than a hook
LogHandler shouldn't be using PascalCase for naming as that should be "reserved" for React components; HandlersComp is fine, since it looks like it's a component
I've looked a lot for that, but never actually found answer for it.
I have a function (not a class) that returns some React.Component and I like using it due to how easy it is to use states. Normally I use classes, but there are some places it just feels better to use a function.
Is there any way to check if the function gets didmounted?
I don't want to create any memory leaks.
Example function:
export default function foo() {
const [bar, setBar] = React.useState(false)
function looping() {
<some code with useState>
}
return (
<div>Something</div>
)
}
You can use useEffect hook:
export default function foo() {
const [bar, setBar] = React.useState(false)
useEffect(() => {
console.log('mounted')
return () => {
console.log('unmounted')
}
}, [])
function looping() {
<some code with useState >
}
return (
<div>Something </div>
)
}
There's also useLayoutEffect, which works synchronously.
Suppose I have a simple file exporting a default function:
// UniqueIdGenerator.js
const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
export default uniqueIdGenerator;
Which I would use like this:
import uniqueIdGenerator from './UniqueIdGenerator';
// ...
uniqueIdGenerator();
I want to assert in my test that this method was called while keeping the original functionality. I'd do that with jest.spyOn however, it requires an object as well as a function name as parameters. How can you do this in a clean way? There's a similar GitHub issue for jasmine for anyone interested.
I ended up ditching the default export:
// UniqueIdGenerator.js
export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
And then I could use and spy it like this:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
Some recommend wrapping them in a const object, and exporting that. I suppose you can also use a class for wrapping.
However, if you can't modify the class there's still a (not-so-nice) solution:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'default');
one could also mock the import and pass the original implementation as mock implementation, like:
import uniqueIdGenerator from './UniqueIdGenerator'; // this import is a mock already
jest.mock('./UniqueIdGenerator.js', () => {
const original = jest. requireActual('./UniqueIdGenerator')
return {
__esModule: true,
default: jest.fn(original.default)
}
})
test(() => {
expect(uniqueIdGenerator).toHaveBeenCalled()
})
Here is a way of doing it for a default export without modifying the import (or even needing an import in the test at all):
const actual = jest.requireActual("./UniqueIdGenerator");
const spy = jest.spyOn(actual, "default");
In some cases you have to mock the import to be able to spy the default export:
import * as fetch from 'node-fetch'
jest.mock('node-fetch', () => ({
default: jest.fn(),
}))
jest.spyOn(fetch, 'default')
Mock only the default export, or any other export, but keep remaining exports in module as original:
import myDefault, { myFunc, notMocked } from "./myModule";
jest.mock("./myModule", () => {
const original = jest.requireActual("./myModule");
return {
__esModule: true,
...original,
default: jest.fn(),
myFunc: jest.fn()
}
});
describe('my description', () => {
it('my test', () => {
myFunc();
myDefault();
expect(myFunct).toHaveBeenCalled();
expect(myDefault).toHaveBeenCalled();
myDefault.mockImplementation(() => 5);
expect(myDefault()).toBe(5);
expect(notMocked()).toBe("i'm not mocked!");
})
});
Use 'default' as the second argument in spyOn function.
import * as MyHelperMethod from '../myHelperMethod';
jest.spyOn(MyHelperMethod, 'default');
What worked for me was a combination of the answer from Janne Annala and OP's own solution. All I wanted to test was that the helper method was called with the correct parameters as I had already written a test for the helper method and it didn't have any bearing on my subsequent test:
// myHelperMethod.js
export const myHelperMethod = (param1, param2) => { // do something with the params };
// someOtherFileUsingMyHelperMethod.js
import * as MyHelperMethod from '../myHelperMethod';
jest.mock('../myHelperMethod', () => ({
myHelperMethod: jest.fn(),
}));
let myHelperMethodSpy = jest.spyOn(MyHelperMethod, 'myHelperMethod');
// ...
// some setup
// ...
test(() => {
expect(myHelperMethodSpy).toHaveBeenCalledWith(param1, param2);
});
Here it is even simpler.
Mock your exported module 'addDelay' (has the sleep function in it) using jest.
const { sleep } = require('../../src/utils/addDelay');
jest.mock('../../src/utils/addDelay', () => {
const delay = jest.requireActual('../../src/utils/addDelay');
return {
...delay,
sleep: jest.fn(),
};});
And the test is as follows and check if sleep function was called with 1 sec as in arg.
test("Should delay 1 second if Okta user has no IDxM Roles", async () => {
// GIVEN
const MockSleep = sleep;
// WHEN
await getUser(req, res);
// THEN
expect(MockSleep).toHaveBeenCalledWith(1000);// sleep(1000): 1sec
});
I know I'm late to the party but I recently had this problem and wanted to share my solution as well ... though it seems a bit more unconventional but could be tweaked by someone with better knowledge.
I happen to have a file with the function that I would like to spy on.
// /foo/ModuleToBeMocked.ts
const fnToSpyOn = () => ...;
export default { fnToSpyOn }
This is then imported into a parent file that would bring, and export, alike functions. Sort of like a classification.
// /parent.ts
import fnToSpyOn from './foo/ModuleToBeMocked';
import someOtherFn from './foo/SomeOtherModule';
...
export { fnToSpyOn, someOtherFn, ... };
And this is how I test the fnToSpyOn
// /foo/ModuleToBeMocked.test.ts
import { ModuleToBeMocked } from '../parent';
const fnToSpyOnSpu = jest.spyOn(ModuleToBeMocked, 'fnToSpyOn');