Got this weird situation. Building for react native and using a native package from Intercom. Importing it works fine when android or ios. But for the web (or node jest) it throws an error.
So have to do some face-pattern "hacking" like this
utilities/Intercom/index.ios.ts
export { default } from '#intercom/intercom-react-native'
utilities/Intercom/index.web.ts
export default function Intercom() {}
some file that uses Intercom
// #ts-ignore
import Intercom from '~/utilities/Intercom' // Cannot find module '~/utilities/Intercom' or its corresponding type declarations.ts(2307)
...
Intercom.logout() // no TS support
Not only does TS complain, but I also loses all types 😭
Is there any other way to do platform specific import and keep the native types?
The error in jest (node) is Cannot read property 'UNREAD_CHANGE_NOTIFICATION' of undefined which is also described in their docs. Problem is that I can't mock it out when using react-native-web that comes with expo.
I think this is what you're looking for https://stackoverflow.com/a/43531355/1868008
In your utilities directory, create a file named Intercom.d.ts and there place the following
import DefaultIos from "./Intercom/index.ios";
import * as ios from "./Intercom/index.ios";
import DefaultWeb from "./Intercom/index.web";
import * as web from "./Intercom/index.web";
declare var _test: typeof ios;
declare var _test: typeof web;
declare var _testDefault: typeof DefaultIos;
declare var _testDefault: typeof DefaultWeb;
export * from "./Intercom/index.ios";
export default DefaultIos;
Not sure what all those are. Maybe something used in typescript internals.
And for the tests, it seems you'll need to mock every method you use in the code you're testing, e.g., in this App component; I'm using the logEvent method, so I return it in the mock object of the library
import React from "react";
import renderer from "react-test-renderer";
import App from "./App";
jest.mock("#intercom/intercom-react-native", () => ({ logEvent: jest.fn() }));
describe("<App />", () => {
it("has 1 child", () => {
const tree = renderer.create(<App />).toJSON();
expect(tree.children.length).toBe(1);
});
});
App.tsx
...
import Intercom from "./utilities/Intercom";
export default function App() {
Intercom.logEvent("test", {});
...
}
For the web implementation, you could import the type to ensure compliance with the library interface
import type { IntercomType } from "#intercom/intercom-react-native";
const webImplementation: IntercomType = {
// here implement all methods
};
export default webImplementation;
https://github.com/diedu89/expo-isomorphic-import-ts
Related
React CRA sometimes can not find react tsx modules.
React version: 18.2.0
Node version: 19.2.0
I've tried to lower Node version. I've tried change export from export * from './ModuleName to export {ModuleName} from './ModuleName and change import from something like import {ModuleName} from ./ModuleName to import {ModuleName} from ./ModuleName/ModuleName. Sometime it helps, sometimes not.
All components written in next pattern:
type ModuleNameProps = {/* some props *.};
export const ModuleName = ({/* props */}: ModuleNameProps): JSX.Element => { /* Component body */};
export * from './ModuleName';
P.S. Application still works with such errors and other developers on a project don't have such errors
P.P.S. Such errors seem to occur only when path to component contains repetitions. For example: './ComponentName/ComponentName/ComponentName.tsx'
Next13 was released a week ago, and I am trying to migrate a next12 app to a next13.
I want to use server-side components as much as possible, but I can't seem to use
import { createContext } from 'react';
in any server component.
I am getting this error:
Server Error
Error:
You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
,----
1 | import { createContext } from 'react';
: ^^^^^^^^^^^^^
`----
Maybe one of these should be marked as a client entry with "use client":
Is there an alternative here or do I have to resort to prop drilling to get server-side rendering?
It seems like I can use createServerContext
import { createServerContext } from 'react';
If you're using Typescript and React 18, you'll also need to add "types": ["react/next"] to your tsconfig.json compiler options, since this is a not-yet-stable function.
This is a new feature from React's SSR to recognize whether a component is client-side or server-side. In your case, createContext is only available on the client side.
If you only use this component for client-side, you can define 'use client'; on top of the component.
'use client';
import { createContext } from 'react';
You can check this Next.js document and this React RFC for the details
According to Next.js 13 beta documentation, you cannot use context in Server Components:
In Next.js 13, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated
However, there are alternative ways to handle data in the new approach, depending on your case. F.e. if you fetched the data from the server in a parent component and then passed it down the tree through Context, you can now fetch the data directly in all the components that depend on this data. React 18 will dedupe (de-duplicate) the fetches, so there are no unnecessary requests.
There are more alternatives in the documentation.
I've made a tiny package to handle context in server components, works with latest next.js, it's called server-only-context:
https://www.npmjs.com/package/server-only-context
Usage:
import serverContext from 'server-only-context';
export const [getLocale, setLocale] = serverContext('en')
export const [getUserId, setUserId] = serverContext('')
import { setLocale, setUserId } from '#/context'
export default function UserPage({ params: { locale, userId } }) {
setLocale(locale)
setUserId(userId)
return <MyComponent/>
}
import { getLocale, getUserId } from '#/context'
export default function MyComponent() {
const locale = getLocale()
const userId = getUserId()
return (
<div>
Hello {userId}! Locale is {locale}.
</div>
)
}
This is the code for it, it's really simple:
import 'server-only'
import { cache } from 'react'
export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
const getRef = cache(() => ({ current: defaultValue }))
const getValue = (): T => getRef().current
const setValue = (value: T) => {
getRef().current = value
}
return [getValue, setValue]
}
I'm trying to get translations from i18n files in my unit testing, I've seen other answers but they work with just one i18n file, My problem is that, I have 2 files and the folder structure is like this,
i18n/en/translation.json
i18n/es/translation.json
and translation.json file is written like this
{... "info":"information", "name":"Name", ...}
doesn't have an export default.
and here is my test file,
import React from 'react'
import '#testing-library/jest-dom'
import {render} from '#testing-library/react'
import AddUsers from '../../components/AddUsers'
test('Render OK',()=>{
const menuLinkUp =false
const component =render(
<AddUsers/>
)
component.getByText(" how can i call my i18n?")
})
I'm using react testing library and jest for doing this.
There is a section in the documentation: https://react.i18next.com/misc/testing.
I would probably mock the react-i18next module, as it requires the least amount of changes.
jest.mock('react-i18next', () => ({
// this mock makes sure any components using the translate HoC receive the t function as a prop
withTranslation: () => Component => {
Component.defaultProps = { ...Component.defaultProps, t: () => "" };
return Component;
},
}));
(If you actually want to "inject" the translations: https://react.i18next.com/misc/testing#example-test-using-this-configuration)
I have this react component. It works just fine for me.
import { Widget } from 'rasa-webchat';
function CustomWidget(){
return (
<Widget
initPayload={"payload"}
socketPath={"/socket.io/"}
customData={{"language": "en"}}
/>
)
}
export default CustomWidget;
But when I try to use it on my next.js website it fails to work.
It gives me a window is not defined error.
I think I resolved this particular error by using the dynamic importer:
import dynamic from "next/dynamic";
const webchat = dynamic(
() => {
return import('rasa-webchat');
},
{ ssr: false }
);
But now I can't figure out how to actually use the widget component from the package.
Am I allowed to import { Widget } from 'rasa-webchat' or is this just not compatible with next.js for some reason? If it's possible, how do I do it?
The syntax for named exports is slightly different. You can use the widget with a dynamic import as follows:
import dynamic from 'next/dynamic';
const Widget = dynamic(
() => import('rasa-webchat').then((mod) => mod.Widget),
{ ssr: false }
);
function CustomWidget(){
return (
<Widget
initPayload={"payload"}
socketPath={"/socket.io/"}
customData={{"language": "en"}}
/>
)
}
export default CustomWidget;
For further details check Next.js dynamic import documentation.
Nextjs is a frame work that allows you to build Static and Server Side rendered apps. So, it uses Nodesj under hood and window is not defined in nodejs. Only way to accessing window in react ssr frameworks is useEffect hook. Your dynamic import solution is right , becuase you are getting file on client side. I hope it makes sense.
Have a great day
I am having some trouble testing components inside App because I am only exporting AppContainer.
const ConnectedApp = connect(mapStateToProps, mapDispatchToProps)(App);
const AppContainer = () => (
<Provider store={clearanceStore}>
<ConnectedApp />
</Provider>
);
export default AppContainer;
How do I test components inside App's return()? This is what I have for a test now which gives an error: Method “simulate” is meant to be run on 1 node. 0 found instead.
test('setSubmit triggered when clicking submit button', () => {
const setSubmit = jest.fn();
const wrapper = shallow(<App />);
const button = wrapper.find('#something');
button.simulate('click');
expect(setSubmit).toHaveBeenCalled();
});
Here is the Redux documentation that should help you:
In a unit test, you would normally import the App component like this:
import App from './App'
However, when you import it, you're actually holding the wrapper component returned by connect(), and not the App component itself. If you want to test its interaction with Redux, this is good news: you can wrap it in a with a store created specifically for this unit test. But sometimes you want to test just the rendering of the component, without a Redux store.
In order to be able to test the App component itself without having to deal with the decorator, we recommend you to also export the undecorated component:
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)
Since the default export is still the decorated component, the import statement pictured above will work as before so you won't have to change your application code. However, you can now import the undecorated App components in your test file like this:
// Note the curly braces: grab the named export instead of default export
import { App } from './App'
And if you need both:
import ConnectedApp, { App } from './App'
More at https://redux.js.org/recipes/writing-tests