What does the following warning mean? - ffi

What does the following warning mean and how can I fix its cause?
Warning: Unimplemented primitive used:removeEventListener
in
[#bs.val]
external addEventListener: (string, unit => unit) => unit = "addEventListener";
external removeEventListener: (string, unit => unit) => unit =
"removeEventListener";
...
React.useEffect(() => {
let handler = () => dispatch(ToggleMenu(false));
addEventListener("popstate", handler);
Some(() => removeEventListener("popstate", handler));
});

You are missing [#bs.val] at the front of removeEventListener when declaring the binding.
[#bs.val]
external addEventListener: (string, unit => unit) => unit = "addEventListener";
[#bs.val]
external removeEventListener: (string, unit => unit) => unit =
"removeEventListener";
Sometimes it's difficult to see this because the linter is always moving lines around ;)
Source: I've also ran into this minutes ago.

Related

Typescript allows to call a function with incorrect parameter's type

There is a function with next signature:
const verify = (address?: string) => void
There is a Component with props type:
type VerifyButtonProps = { onClick: () => void; }
There is a Component with props type:
type TButtonProps = { onClick?: React.MouseEventHandler<HTMLButtonElement>; children: React.ReactNode; };
[Codesanbox example]
(https://codesandbox.io/s/react-ts-playground-forked-v24gs7?file=/src/index.tsx/)
I'm getting the runtime error when click on the button and expect typescript points out to it, but compilation passes without any errors.
How can I prevent runtime error with help of typescript on the compiling step?
Your issue is basically following case (playground):
const verify = (address?: string) => address?.toLowerCase()
const verifyEmpty: () => void = verify
const onClick: (event: object) => void = verifyEmpty
onClick({ this: 'is not a string'})
Typescript allows each of these steps, however combined it produces a runtime error. This unsoundness is known, however Typescript does not guarantee soundness (no runtime errors if if there are no type errors) and this is one case where they decided to leave it unsound.
This means it is up to you to catch such errors. In your case, you could use onClick={() => verify()} to fix the error.
To avoid this situation you can replace
() => void
with
(...args: undefined[]) => void;
With that replacement you'll explicitly tell to your component, that function doesn't allow any number of arguments.
So, you can still pass verify function to your component. But inside of the component you can't pass it down to any function props with optional arguments, e.g. <Button onClick={verify} />
From the index.tsx file, the problem with your code is that your trying to run .toLowerCase() on an event.
Here is your code:
const verify = (address?: string) => { console.log("address = ", address?.toLowerCase());};
const App = (props) => {
return <VerifyButton onClick={verify} />;
};
I suggest you look into handlers but passing your function as you have in the onClick handler means that you get every argument passed to the verify function as address.
Log the address to console and see what I mean.
You may write your change handlers this way:
onClick={(e) => yourFunction(e)}
This is useful if you need something from the event, for example a value from an input.
OR
onClick={() => yourFunction()}
This will prevent you from passing unwanted arguments to your functions. Hope this helps.
u need to correctly type the verify function to match the expected onClick prop type in each component.
For VerifyButtonProps, the verify function can be passed like:
const VerifyButton: React.FC<VerifyButtonProps> = ({ onClick }) => (
<button onClick={onClick}>Verify</button>
);
const App = () => {
const handleVerify = () => {
verify();
};
return (
<div>
<VerifyButton onClick={handleVerify} />
</div>
);
};
For TButtonProps, the verify function needs to be converted to a proper React.MouseEventHandler:
const TButton: React.FC<TButtonProps> = ({ onClick, children }) => (
<button onClick={onClick}>{children}</button>
);
const App = () => {
const handleVerify = (event: React.MouseEvent<HTMLButtonElement>) => {
verify();
};
return (
<div>
<TButton onClick={handleVerify}>Verify</TButton>
</div>
);
};
when u make these changes, TypeScript will catch the type mismatch and display an error during the compilation step, rather than at runtime.

How can I best use rxjs to perform multiple fetch operations in a single action using a forEach approach

I am new to React and tasked with modifying an existing application which already performs multiple api calls. The pattern that is in use in the app is demonstrated by the below example. EG if an action mapperActions.selectPolygon of type CaseReducerActions<CaseReducers> is raised, then the below code will execute, calling the fetchPolygon function to retrieve data asynchronously from an AWS lambda and the returned result will be processed by the fetchPolygonCompleted function.
let fetchPolygonStatsEpic = (action$: any, state$: StateObservable<RootState>) => action$.pipe(
ofType(
mapperActions.selectPolygon.type,
mapperActions.alterQueryIndexname.type,
),
filter(() => state$.value.mapper.selectedPolygon !== undefined),
switchMap(() =>
concat(
of(globalActions.startLoading('polygon')),
fetchPolygon(state$.value.mapper).pipe(
map(result => mapperActions.fetchPolygonCompleted(result)),
catchError(e => of(globalActions.errorOccurred(e.message)))),
of(globalActions.stopLoading('polygon')),
)
)
)
The change I need to implement is as follows:
I have a type 'Framework'...
export type Framework = {
....
}
...a collection of these...
export const frameworks: {[framework: string]: Framework} = {
liveng0: {
...
},
liveng1: {
...
},
habmosCairngorms: {
...
},
spaceintCairngorms: {
...
}
}
..and an API method that retrieves data for a specified Framework...
export let fetchHabitats = (requiredFramework: Framework): Observable<FrameworkHabitats> => {
...
}
When the action mapperActions.initialise of type CaseReducerActions<CaseReducers> is raised, I need to execute a block of code which will execute and process 4 separate asynchronous API calls to fetch the data for each of the 4 framework types above in turn.
A naive approach of simply copy-and-pasting the API call for each framework in turn within the app's establised pattern for fetching data actually works correctly and produces the results I require, ie:
let fetchHabitatsEpic = (action$: any, state$: StateObservable<RootState>) => action$.pipe(
ofType(
mapperActions.initialise.type,
),
switchMap(() =>
concat(
of(globalActions.startLoading('habitats')),
fetchHabitats(frameworks.liveng0).pipe(
map(result => mapperActions.fetchHabitatsCompleted(result)),
catchError(e => of(globalActions.errorOccurred(e.message)))),
fetchHabitats(frameworks.liveng1).pipe(
map(result => mapperActions.fetchHabitatsCompleted(result)),
catchError(e => of(globalActions.errorOccurred(e.message)))),
fetchHabitats(frameworks.habmosCairngorms).pipe(
map(result => mapperActions.fetchHabitatsCompleted(result)),
catchError(e => of(globalActions.errorOccurred(e.message)))),
fetchHabitats(frameworks.spaceintCairngorms).pipe(
map(result => mapperActions.fetchHabitatsCompleted(result)),
catchError(e => of(globalActions.errorOccurred(e.message)))),
of(globalActions.stopLoading('habitats')),
)
)
)
Although the above works as required, it clearly needs to be replaced with a forEach approach that operates over each Framework in turn and treats each API call atomically, whilst still being able to trigger all 4 from a single action. Could you please explain the syntax required to achieve this?
You can use Object.values() to iterate over the frameworks and Array.map() to map them to API calls, as follows:
concat(
of(globalActions.startLoading('habitats')),
...Object.values(frameworks).map(framework =>
fetchHabitats(framework).pipe(
map(result => mapperActions.fetchHabitatsCompleted(result)),
catchError(e => of(globalActions.errorOccurred(e.message)))
)
)
)

React Testing Library Unit Test Case: Unable to find node on an unmounted component

I'm having issue with React Unit test cases.
React: v18.2
Node v18.8
Created custom function to render component with ReactIntl. If we use custom component in same file in two different test cases, the second test is failing with below error.
Unable to find node on an unmounted component.
at findCurrentFiberUsingSlowPath (node_modules/react-dom/cjs/react-dom.development.js:4552:13)
at findCurrentHostFiber (node_modules/react-dom/cjs/react-dom.development.js:4703:23)
at findHostInstanceWithWarning (node_modules/react-dom/cjs/react-dom.development.js:28745:21)
at Object.findDOMNode (node_modules/react-dom/cjs/react-dom.development.js:29645:12)
at Transition.performEnter (node_modules/react-transition-group/cjs/Transition.js:280:71)
at node_modules/react-transition-group/cjs/Transition.js:259:27
If I run in different files or test case with setTimeout it is working as expected and there is no error. Please find the other configs below. It is failing even it is same test case.
setUpIntlConfig();
beforeAll(() => server.listen());
afterEach(() => {
server.resetHandlers();
});
afterAll(() => {
jest.clearAllMocks();
server.close();
cleanup();
});
Intl Config:
export const setUpIntlConfig = () => {
if (global.Intl) {
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
} else {
global.Intl = IntlPolyfill;
}
};
export const RenderWithReactIntl = (component: any) => {
return {
...render(
<IntlProvider locale="en" messages={en}>
{component}
</IntlProvider>
)
};
};
I'm using msw as mock server. Please guide us, if we are missing any configs.
Test cases:
test('fire get resource details with data', async () => {
jest.spyOn(SGWidgets, 'getAuthorizationHeader').mockReturnValue('test-access-token');
process.env = Object.assign(process.env, { REACT_APP_DIAM_API_ENDPOINT: '' });
RenderWithReactIntl(<AllocatedAccess diamUserId={diamUserIdWithData} />);
await waitForElementToBeRemoved(() => screen.getByText(/loading data.../i));
const viewResource = screen.getAllByText(/view resource/i);
fireEvent.click(viewResource[0]);
await waitForElementToBeRemoved(() => screen.getByText(/loading/i));
const ownerName = screen.getByText(/benedicte masson/i);
expect(ownerName).toBeInTheDocument();
});
test('fire get resource details with data----2', async () => {
jest.spyOn(SGWidgets, 'getAuthorizationHeader').mockReturnValue('test-access-token');
process.env = Object.assign(process.env, { REACT_APP_DIAM_API_ENDPOINT: '' });
RenderWithReactIntl(<AllocatedAccess diamUserId={diamUserIdWithData} />);
await waitForElementToBeRemoved(() => screen.getByText(/loading data.../i));
const viewResource = screen.getAllByText(/view resource/i);
fireEvent.click(viewResource[0]);
await waitForElementToBeRemoved(() => screen.getByText(/loading/i));
const ownerName = screen.getByText(/benedicte masson/i);
expect(ownerName).toBeInTheDocument();
});
Can you try these changes:
test('fire get resource details with data----2', async () => {
jest.spyOn(SGWidgets, 'getAuthorizationHeader').mockReturnValue('test-access-token');
process.env = Object.assign(process.env, { REACT_APP_DIAM_API_ENDPOINT: '' });
RenderWithReactIntl(<AllocatedAccess diamUserId={diamUserIdWithData} />);
await waitFor(() => expect(screen.getByText(/loading data.../i)).not.toBeInTheDocument());
const viewResource = screen.getAllByText(/view resource/i);
act(() => {
fireEvent.click(viewResource[0]);
});
await waitFor(() => expect(screen.getByText(/loading/i)).not.toBeInTheDocument());
expect(screen.getByText(/benedicte masson/i)).toBeVisible();
});
I've got into the habit of using act() when altering something that's visible on the screen. A good guide to here: https://testing-library.com/docs/guide-disappearance/
Using getBy* in the waitFor() blocks as above though, you may be better off specifically checking the text's non-existence.
Without seeing your code it's difficult to go any further. I always say keep tests short and simple, we're testing one thing. The more complex they get the more changes for unforeseen errors. It looks like you're rendering, awaiting a modal closure, then a click, then another modal closure, then more text on the screen. I'd split it into two or more tests.

Integrate DeepAR Shoes try-on SDK with react

I am trying to implement DeepAR SDK for shoe try-on in my react app but when I try to instantiate DeepAR class I got a weird error:
Uncaught (in promise) RuntimeError: Aborted(both async and sync fetching of the wasm failed). Build with -sASSERTIONS for more info.
at q2 (deepar.esm.js:1:676382)
at Q2 (deepar.esm.js:1:676689)
at deepar.esm.js:1:755786
I tried to put those files in public folder, and inside src but it didn't work.
const deepar = new DeepAR({
canvas,
licenseKey: import.meta.env.VITE_DEEPAR_SDK_KEY,
deeparWasmPath: "../lib/wasm/deepar.wasm",
footTrackingConfig: {
poseEstimationWasmPath: "../lib/wasm/libxzimgPoseEstimation.wasm",
detectorPath: iosDetected
? "../lib/models/foot/foot-detector-ios.bin"
: "../lib/models/foot/foot-detector-android.bin",
trackerPath: iosDetected
? "../lib/models/foot/foot-tracker-ios.bin"
: "../lib/models/foot/foot-tracker-android.bin",
objPath: "../lib/models/foot/foot-model.obj",
},
callbacks: {
onInitialize: () => {
deepar.startVideo();
// deepar.switchEffect(0, "mask", "/effects/Shoe_PBR");
},
onCameraPermissionAsked: () => console.log("onCameraPermissionAsked"),
onCameraPermissionGranted: () => console.log("onCameraPermissionGranted"),
onCameraPermissionDenied: () => console.log("onCameraPermissionDenied"),
onVideoStarted: () => console.log("onVideoStarted"),
onError: (error) => console.log("onError", error),
},
});

How do you mock isPlatform from #ionic/core

Have a React Ionic app where I need to determine if the app is running on an iOS device. I've done that by importing isPlatform.
import { isPlatform } from "#ionic/core";
const isIOS = isPlatform("ios");
I've tried the following and when isPlatform is called it still returns false.
jest.mock("#ionic/core", () => ({
isPlatform: () => {
return true;
}
}));
How do I mock isPlatform in my unit test using jest so it returns true?
Figured it out. I needed to mock ionic/core in order for it to work.
jest.mock("#ionic/core");
import * as Ionic from '#ionic/core';
(Ionic as any).isPlatform = jest.fn(() => true);
If component only uses isFlatform in #ionic/core you can mock one function isFlatform:
jest.mock("#ionic/core", () => ({
isPlatform: () => true,
}));
but when component use another function and you only want to mock isFlatform you can use:
jest.mock("#ionic/core", () => ({
...jest.requireActual("#ionic/core"),
isPlatform: () => true,
}));
Similar to the previous responses but using a slightly more flexible approach to be able to mock the result based on what we want to test in each scenario. In our app we're using Angular but this approach should be working as well for React.
The idea is to define a mockIsPlatform() function:
// Note: on our app, we're importing the `isPlatform` helper method
// from `#ionic/angular` but that's not really important.
let mockIsPlatform: (key: string) => boolean;
jest.mock('#ionic/angular', () => ({
...(jest.requireActual('#ionic/angular') as object),
isPlatform: (key: string) => mockIsPlatform(key),
}));
Our service has some methods that use the isPlatform() method behind the scenes:
public isIos(): boolean {
return isPlatform('ios');
}
public isAndroid(): boolean {
return isPlatform('android');
}
So we can now test those methods like this:
test('detect platform', () => {
// Simulate ios platform
mockIsPlatform = (key) => key === 'ios';
expect(myServiceInstance.isIos()).toBeTruthy();
expect(myServiceInstance.isAndroid()).toBeFalsy();
// Simulate android platform
mockIsPlatform = (key) => key === 'android';
expect(myServiceInstance.isIos()).toBeFalsy();
expect(myServiceInstance.isAndroid()).toBeTruthy();
})

Resources