简体   繁体   中英

Typescript React Dynamic import with Router

Background

I'm currently trying to restructure a webapp I made to using typescript, react, and utilizing webpack to bundle (it was previously html+typescript). After the headache of getting webpack to work with my application and to bundle my.tsx files, I am now trying to implement routing with dynamic imports, as it's a site that I will use to host multiple separate projects. I have filled in most of these files with some test data just to test it working

Problem

I'm getting a runtime error client side, like below for each component that I'm attempting to dynamically import

Uncaught Error: Element type is invalid. Received a promise that resolves to: [object Object]. Lazy element type must resolve to a class or function.

Attempted solutions

There was a wide variety of articles I tried replicating to solve the issue to no avail. I then started reading up on documentation and things still weren't clicking. As you will see below, I tried abstracting the dynamic imports to their own react components wrapped in an actual element, and that still wasn't working. I'd prefer to use react-router-dom for routing for the simple reason being I'm familiar with it, and according to external resources, this should be possible.

Code

Index.tsx

import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import {
    BrowserRouter as Router,
    Route,
    Routes,
    Outlet
} from 'react-router-dom';
import ReactLoading from 'react-loading';

const BudgetApp = React.lazy(() => import("./HomeServer/Envelopes"));
const HomePage = React.lazy(() => import("./Home"));

function Loading() {

    return (
        <span>
            <ReactLoading type={"bars"} color={"#51B9D3"} height={'20%'} width={'20%'} /> <br />
            Loading...
        </span>
    )
}

function HomePageRoute() {
    return (
        <React.Suspense fallback={<Loading />}>
            <HomePage />
        </React.Suspense>
    )
}

function BudgetAppRoute() {

    return (
        <React.Suspense fallback={<Loading />}>
            <BudgetApp />
        </React.Suspense>
    )
}

function RouteWrapper(): React.ReactNode {

    return (
        <StrictMode>
            <div>
                <h1>Content</h1>
                <Router>
                    <Routes>
                        <Route path="/" element={<AppLayout />} />
                        <Route index element={<HomePageRoute />} />
                        <Route path="/budget" element={<BudgetAppRoute />} />
                    </Routes>
                </Router>
            </div>
        </StrictMode>
    )
}

const AppLayout = () => {
    return (
        <div>
            Test display
            <Outlet />
        </div>
    )
}

const app = RouteWrapper();
const root = ReactDOM.createRoot(document.getElementById('root') as Element);
root.render(app);

Home.tsx

import React, { FunctionComponent } from 'react';
import { NavLink } from 'react-router-dom'
import styled from 'styled-components';
import Card from 'react-bootstrap/Card';
import Button from 'react-bootstrap/Button';


type project_component = {
    title: string,
    description: string,
    status?: string,
    link: string,

}

function ProjectComponent(props: project_component) {

    return (
        <Card className="text-center">
            <Card.Header>Featured</Card.Header>
            <Card.Body>
                <Card.Title>{props.title}</Card.Title>
                <Card.Text>
                    {props.description}
                </Card.Text>
                <NavLink to={props.link}><Button variant="primary">Open</Button></NavLink>
            </Card.Body>
            <Card.Footer className="text-muted">{props.status}</Card.Footer>
        </Card>
    );
}

function ProjestList() {
    const List = styled.div`
    width: 75vw;
    height: 100vh;
    margin: auto;
    text-align: center;
    `
    const krogerDescription = "An app built to build off Kroger's available dataset for better real-time updates with a dedicated server to host it for my wife and I."
    const budgetDescription = "An app built for viewing both high and low level reports of how our budgeting is going. Used mostly to build on my data analytics skills for reporting"
    return (
        <List>
            <ProjectComponent title="Kroger Grocery App" description={krogerDescription} link="/grocery" status="v1 ready" />
            <ProjectComponent title="Budgeting App" description={budgetDescription} link="/budget" status="v1 WIP"/>
        </List>
    )
}

const App = ProjestList();
export default App as unknown as FunctionComponent;

Envelopes.tsx

import {FunctionComponent} from 'react';
import React from 'react'
import ReactDOM from 'react-dom/client';

function Test(props: {hello:string}) {

    return (<div>{props.hello}</div>)
}

function Body() {

    return (
        <div>
            hello
            <Test hello="jfjjf"/>
        </div>
    )
}


export default Body as FunctionComponent;

Webpackconfig

If it's of any use, here's the config file I have for webpack.

const path = require('path');

module.exports = {
  entry: './src/Index.tsx',
  mode: "development",
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: '[name].bundle.js',
    chunkFilename: '[name].bundle.js',
    path: path.resolve(__dirname, 'public/js'),
    publicPath: 'dist/'
  }
};

Y'all. I found the answer right as I finished writing this. I figured there was no way I was just gonna waste a perfectly good question for future me to stumble upon or some other unlucky soul.

It turns out, my problem lied within Home.tsx . At the bottom of the file, I called this line:

const App = ProjestList();
export default App as unknown as FunctionComponent;

Instead, I needed to export the COMPONENT... not the object returned from it. Which is funny, because in the other file I did it correctly. It should look like this:

export default ProjestList as FunctionComponent;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM