TypeScript module omitted when only used as an alias - module

I have an import statement of a NPM module like this:
import * as ReactRouter from 'react-router'
Which transpiles using module: "commonjs" to:
var ReactRouter = require('react-router');
And I have access to ReactRouter.Router, etc. in my JSX. For example, this works:
render(){
return (
<ReactRouter.Router>
<ReactRouter.Route path="/" component={App}>
<ReactRouter.IndexRoute component={Index} />
<ReactRouter.Route path="detail/:id" component={Detail} />
{/* etc */}
</ReactRouter.Route>
</ReactRouter.Router>
)
}
However, I want to create aliases to Route, etc. so I don't have to refer ReactRouter.* everywhere:
import * as ReactRouter from 'react-router'
import Router = ReactRouter.Router
import Route = ReactRouter.Route
import IndexRoute = ReactRouter.IndexRoute
render(){
return (
<Router>
<Route path="/" component={App}>
<IndexRoute component={Index} />
<Route path="detail/:id" component={Detail} />
{/* etc */}
</Route>
</Router>
)
}
But when I do this, the transpiled code becomes this:
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var IndexRoute = ReactRouter.IndexRoute;
Notice the ReactRouter module itself is nowhere, and at runtime it breaks because ReactRouter is undefined. However, if I stick at least one arbitrary reference to ReactRouter it shows up:
// TS
import * as ReactRouter
import Router = ReactRouter.Router
ReactRouter // reference to force ReactRouter module to be compiled
// Transpiles to:
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
ReactRouter;
And then it works at runtime.
So in other words, it seems that an import alias does not count as a reference, even though it really is. Is this a TSC bug? Is there a workaround other than creating an extra reference to the module so that TSC doesn't omit it? Is there a better way to import these individual symbols from the react-router module, like import {Router, Route, IndexRoute} from 'react-router' can be done with Babel?

This is a compiler bug. It was fixed a little while ago and you can either npm install typescript#next or use TypeScript 1.7 once it becomes available.
Referencing ReactRouter in an expression is the best workaround.

Related

route function from Preact-Router does not update window location or component

I am trying to use the route function from preact-router in order to sanitize a hash prefix that I add. However, the route function does not seem to update the window location or the component that is rendered by the Router. Am I using this function wrong?
I am hosting my react build in an S3 bucket and using their redirecting rules to add a #! in between the base url and the path. Would using hash history instead of browser history and what are the downsides of hash history?
documentation
import { Router, route } from 'preact-router';
import Header from './header';
// Code-splitting is automated for `routes` directory
import Home from '../routes/home';
import Profile from '../routes/profile';
import Library from '../routes/library';
import LibraryCreator from '../routes/libraryCreator';
/**
* Removes the #! injected into the url
* A #! is prefixed to all requests sent to our S3 instance so that we send the index no matter what path is requested
* This allows the Router component to handle Routing and prevents 404/403 errors from requesting files which don't exist
* [More Info](https://via.studio/journal/hosting-a-reactjs-app-with-routing-on-aws-s3)
*/
const sanitizeHashPrefix = () => {
const path = (/!(\/.*)$/.exec(location.hash) || [])[1];
if (path) {
route(path, true);
}
}
const App = () => (
<div id="app">
<Header />
<Router onChange={sanitizeHashPrefix()}>
<Home path="/" />
<Profile path="/profile/" user="me" />
<Profile path="/profile/:user" />
<Library path="/library/:library" />
<LibraryCreator path="/library/newLibrary" />
</Router>
</div>
)

How to use hooks in custom plugins

I am trying to write a custom plugin for our Docusaurus site. I am able to wire up the custom component, but I cannot use hooks like useState or useEffect. The page crashes saying I'm using an invalid React hook.
I know its possible to use hooks because I see other plugins doing it so I'm sure its a syntax problem somewhere.
Here's my code:
index.ts
import path from 'path'
module.exports = function () {
return {
name: 'docusaurus-theme-myorg-technology',
getThemePath() {
return path.resolve(__dirname, './theme')
}
};
};
theme/index.tsx
import React from 'react'
import {CustomTOC} from './CustomTOC'
const WrappedTOC = (props: any) => {
return (
<CustomTOC {...props} />
);
};
export default WrappedTOC;
theme/CustomTOC.tsx
import React, { useState } from 'react';
import TOC from '#theme-init/TOC';
export default function CustomTOC(props: any) {
//const [tags, setTags] = useState<any[]>([]); <-- if I comment this out the page crashes
return (
<>
<TOC {...props} />
Hello world
</>
);
}
"Invalid hooks call" link to a doc page, that you should read carefully.
Most likely: you are using a different version of React for your component lib that the one Docusaurus uses internally, and it leads to the React lib being used twice at runtime. Make sure the final project will only include one React version. You can for example use the exact same version that the one Docusaurus uses

React-native-web Multi-Platform Setup using Expo 44 + Typescript

What is the simplest way to implement Multi-Platform Setup for a component in Expo. I have tried mamy diferent ways.. it was working on web but it is failing on Native and failing with Jest & #testing-library/react-native. Ideally I would like the least amount of custom config etc (do not want to eject). I expect the file structure to look like this:
Component
|- index.tsx
|- Component.native.tsx
|- Component.web.tsx
I am not sure how to do the index.tsx. I saw someone say something like this would work:
// index.tsx
// #ts-ignore
export { default } from "Component"
this didn't work so I did
// index.tsx
// #ts-ignore
export { default } from "./Component"
This worked for web, but the jest test said
Cannot find './Component'
However, Jest was able to find:
'./Component.mobile.tsx'
'./Component.web.tsx'
I tried:
// index.tsx
// #ts-ignore
import Component from "./Component";
export default Component
and the tests was the same
and the native emulator said:
Unable to resolve module ./Component
I tried using lazy loading but this does not work on web.
import { lazy, Suspense } from "react";
import { Platform } from "react-native";
import Loading from "../../components/Loading";
import { ComponentType } from "./types";
const Web = lazy(() => import("./Component.web"));
const Mobile = lazy(() => import("./Component.mobile"));
const Component: ComponentType = (props) => {
const isWeb = Platform.OS === "web";
return (
<Suspense fallback={<Loading message="Loading Component" />}>
{isWeb ? <Web {...props} /> : <Mobile {...props} />}
</Suspense>
);
};
export default Component
Questions
how to use diferent files for components depending on platform (exlude other files from build)
how to make it ok with ts in vscode
Using Expo 44. Thanks
I would use named exports. So begin by having the same component name in both files. Next I would have one file called Component.tsx and the other Component.native.tsx. That will be enough to allow the bundler to pull the native for native and the other for non-native (in other words web). That should be all you need.

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) in react native project

I am building a react native project I get the following error:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of SignInScreen.
Here is my code.
import React from 'react';
import { View,Image,StyleSheet,useWindowDimensions } from 'react-native';
import Logo from'../../../assetss/images/logo.png';
import CustomInput from '../../components/CustomInput';
const SignInScreen = () => {
const {height} = useWindowDimensions();
return (
<View style= {styles.root}>
<Image source={Logo} style ={[styles.logo, {height: height * 0.3}]}
resizeMode="contain" />
<CustomInput />
</View>
);
};
const styles = StyleSheet.create({
root: {
alignItems: 'center',
padding: 20,
},
logo:{
width: 1000,
maxWidth: 1100,
maxHeight: 200 ,
}
})
export default SignInScreen
There is a difference between named exports and default exports.
Named Export
Can be used for top level values of a module, e.g.
export const fetch = () => {
}
export const Component = () => {
return <></>
}
The above can be imported using
import { fetch, Component } from "./MyModule"
Notice the curly braces.
Default Export
A default export can only be used once per file, e.g.
const Component = () => {
return <></>
}
export default Component;
The above can be imported using
import Component from "./MyModule"
Notice that we have not used curly braces here.
The error message
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
tells us that we have mixed this up for some of our imports. Your imports for View, Image, StyleSheet and useWindowDimensions are correct. Hence, either CustomInput or Logo is not correctly imported (or exported; it depends on how we want to see it).
You are assuming default exports for both. Check if they are exported using default export.
If you do not want to use default exports, then you need to change your import statements to the following.
import { CustomInput } from '../../components/CustomInput';
Furthermore, check that the import paths, e.g. '../../../assetss/images/logo.png' and '../../components/CustomInput', are correct.
In my case the problem was I created all components but didn't save the files. I don't use autosave, so I created all components, imported them to my Home component, but they were not saved so that error was thrown. Once I saved each file, the error disappeared.
Sounds like you had an index.js file in your CustomInput folder, so when you wrote
import CustomInput from './../../components/CustomInput';
it thought you wanted
import {CustomInput} from './../../components/CustomInput/index.js';
but really you wanted
import CustomInput from './../../components/CustomInput/CustomInput.js';
This error
"Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of ..."
shows up for many different types of errors that directly or indirectly eventually boil down to export/import default/named. Besides your case where it thought you were looking in index.js for a {named} in curly braces export/import, you can also get that error message if you accidentally misspell your named import, for example: import {Flatlist} with a lowercase l instead of import {FlatList} with an uppercase L.
thanks for your help my probl/em is solved b/y just putting .js . For example import CustomInput from './../../components/CustomInput/CustomInput.js
From the code you have shown, it seems like assets is spelt wrong. You can check if the actual folder is spelt like that.
Faced while unit test in react
Notices the same issue while writing unit test case using jest and react-testing-library.
Problem: It may be due to wrong way of mocking the component using jest.mock
Problem Situation:
jest.mock('./components/notes-list', () => {
return {
NotesList: () => <div data-testid='note-list-component'>Hello World</div>,
};
});
Solution
jest.mock('./components/notes-list', () => {
const NotesList = () => (
<div data-testid='note-list-component'>Hello World</div>
);
return NotesList;
});
This error message is indicating that there is an issue with a component in your React application. Specifically, React is expecting the component to be either a string (for built-in components such as div or span) or a class or function (for custom components defined by you). However, in this case, the component is undefined.
There are a few potential causes for this error:
Forgetting to export the component: If the component is defined in a separate file, make sure that it is properly exported at the end of the file. For example:
// MyComponent.js
import React from 'react';
class MyComponent extends React.Component {
render() {
return <div>Hello, World!</div>;
}
}
export default MyComponent;
Mixing up default and named imports: If you are importing the component into another file, make sure that you are using the correct syntax. For example:
// App.js
import React from 'react';
import MyComponent from './MyComponent';
function App() {
return <MyComponent />;
}
export default App;
If you're still encountering the error after checking these potential causes, it may be helpful to look at the surrounding code to see if there is anything else that could be causing the issue. Additionally, make sure that the version of React that you are using is compatible with the version of react-dom that you have installed.

Reactnative error - expected a string or a class/function but got: undefined

I am hitting this error when i am trying to define my own custom components.
// /common/MyAppText.js
import React, {Component} from 'react';
import {
Text,
View,
} from 'ReactNative';
class MyAppText extends Component {
render(){
return (
<View>
<Text>hello</Text>
</View>
)
}
}
export default MyAppText
On the other app, i tried to import it and use it by
import MyAppText from './common/MyAppText'
class Home extends Component {
render(){
return (
<View>
<MyAppText />
</View>
)
}
}
But i hit the error "expected a string or a class/function but got: undefined, please check render method of 'MyAppText'. Anyone can see what is wrong with the export syntax?
If i defined everything in the same document then it works, so it is something with the export that i couldn't figure out.
Your own export/import looks fine. Not sure if this is the issue, but the line
import {..} from 'ReactNative';
Should be:
import {..} from 'react-native';
You might expect that to crash with a different error (module not found), but since this internal React Native file exports a globally available module "ReactNative" via Haste, your import ends up picking that file. Because that file doesn't export properties View and Text, the code compiles fine, but ends up with undefined variables.
Edit for more context:
The React Native bundler (called Metro) uses Facebook's own module system (called Haste), which allows anybody to decorate a file with a comment #providesModule Name, and then import it from globally anywhere with just import ... from 'Name';
One of the internal renderer modules declares #providesModule ReactNative. So when you've imported from 'ReactNative', you got that module instead of a build error.