Search code examples
javascriptreactjstypescriptnext.jsreact-typescript

Lesson cannot be used as JSX Component in Next JS TypeScript


Below is my page.tsx

import Aspects from '@/components/Aspects';
import FreeForm from '@/components/FreeForm';
import Lesson from '@/components/Lesson'; 
import React from 'react';
import { Route, Routes } from 'react-router-dom'; 

const Home: React.FC = () => {
   return (
     <div>
       <Routes>
         <Route path="/" element={<Aspects />} />
         <Route path="/freeform" element={<FreeForm />} />
         <Route path="/lesson" element={<Lesson />} />
       </Routes>
     </div>
   );
 };
 export default Home;

And I just copied my react js using javascript coding to next js typescript, and here is the code for Aspects.tsx, FreeForm.tsx and Lesson.tsx Aspects.tsx

import './Aspects.css';
import IMG1 from '../stories/assets/Picts/10.jpg';
import IMG2 from '../stories/assets/Picts/9.jpg';
import IMG3 from '../stories/assets/Picts/8.jpg';
import React, { useState, } from 'react';
import { useNavigate } from 'react-router-dom';
import { notification } from 'antd';
import { SmileOutlined } from '@ant-design/icons';
import Image from 'next/image';
// import { useRouter } from 'next/router';



function Aspects() {
    const navigate = useNavigate(); 
  
  const [check, setcheck] = useState('');
  const [isClosed, setIsClosed] = useState(false);

  const handleClick = () => {
    if (!check || !document.querySelector('input[name="choosingMethod"]:checked')) {
      notification.open({
     message: 'Heii learner!',
      description: 'You must choose both the Aspect Ratio and Content Creation Method first',
      icon: <SmileOutlined />,
    });
    } else {
      navigate('/freeform');
    }
  };

    const handleClose = () => {
      setIsClosed(true);
    };
  
    if (isClosed) {
      
      return null;
    }

    
  return (
    <div className="container-ratio">
        <div className="container-title">
            <h3>How you want to process your course?</h3>
            <p>Please select the ratio and method of content creation</p>
            <button type="button" className="btn-close" aria-label="Close" onClick={handleClose}></button>
        </div>

        <h2>Aspec Ratio</h2>
    <div className="container-card">
        <div className="card">
            <Image src={IMG1} className="card-img-top" alt="..." />
            <div className="card-body" >
                <h3>Free Form</h3>
                <p className="card-text">Modern formats keep your content connected and fluid.</p>
            </div>
        </div>
        
        <div className="card">
            <Image src={IMG2} className="card-img-top" alt="..." />
            <div className="card-body">
                <h3>16: 9</h3>
                <p className="card-text">Modern formats help keep your content connected and consistent.</p>
            </div>
        </div>
        <div className="card">
            <Image src={IMG3} className="card-img-top" alt="..." />
            <div className="card-body">
                <h3>9:16</h3>
                <p className="card-text">Suitable for modern contents, micro-courses, and social media, especially Tiktok, Reels..</p>
            </div>
        </div>
     
    </div>
    <div className="container-method">
        <h3>Content Creation Method</h3>
        <div className="row">
            
              <div className="card">
              <label htmlFor="autoOption">
                <div className="card-body d-flex align-items-center">
                    <input type="radio" id='autoOption' name="choosingMethod" value="automatic" />
                    <div className="text">
                    <h5 className="card-title">Automaticly</h5>
                  <p className="card-text">AI will generate the course based on your outline.</p>
                </div>
                </div>
                </label>
              </div>
              <div className="card">
          <label htmlFor="manualOpt">
            <div className="card-body d-flex align-items-center">
              <input type="radio" id="manualOpt" name="choosingMethod" value="manual" className="me-3" />
              <div>
                <h5 className="card-title">Manually</h5>
                <p className="card-text">...</p>
              </div>
            </div>
          </label>
        </div>
          </div>
    </div>
    <div className="d-flex justify-content-center align-items-center mt-4">
    <button type="button" className="btn btn-success"  onClick={handleClick}>Next</button>
</div>
</div>
  )
}

export default Aspects

FreeForm.tsx code from my react js, it was FreeForm.js but I convert it using .tsx when I pasted it in the next.js typescript.

import React from 'react';
import './FreeForm.css';
import IMG1 from '../stories/assets/Picts/10.jpg';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { notification } from 'antd';
import { SmileOutlined } from '@ant-design/icons';
import Image from 'next/image';
import { useRouter } from 'next/router';

function FreeForm() {
  const toVideo = useRouter();
  const toAspects = useRouter();

  const [choice, setChoice] = useState('');
  const [closed, setClosed]= useState(false);

    const handleCloseBtn = () => {
      setClosed(true);
    }
    if(closed) { return null}

    const handleClick = () => {
      if (!choice) {
        notification.open({
        message: 'Heii learner',
        description: 'You must choose the template before go to the next step',
        icon: <SmileOutlined/>,
      });
      } else {
        toVideo.push('/video');
      }
    };
  
    
    
  return (
    <div className="container-ratio">
        <div className="container-title">
            <h3>Select a template</h3>
            <p>Here is the templates that support Free form</p>
            <button type="button" className="btn-close" aria-label="Close" onClick={handleCloseBtn}></button>
        </div>
        
  <div className="row container-baru">
    <div className="col-md-4 mb-3">
      <div className="card" >
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
        <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card" >
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card" >
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card" >
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card" >
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card">
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card" >
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 1</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card">
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 2</h5>
        </div>
      </div>
    </div>
    <div className="col-md-4 mb-3">
      <div className="card">
        <div className="card-body">
        <Image src={IMG1} className="card-img-top" alt="..."/>
          <h5 className="card-title">Card 3</h5>
        </div>
      </div>
    </div>
  </div>
  <div className="make-buttons-center">
        <div className="container-buttons">
        <button type="button" className="btn tombol-kembali btn-light" onClick={() => toAspects.push("/")}>
          Back
        </button>
        <button type="button" className="btn tombol-generate btn-success" onClick={handleClick}>
          Generate Now
        </button>
        </div>
      </div>

    </div>

  )
}
export default FreeForm

And the last one is lesson.tsx, the code is below:

import Next from 'next';
import { Rate, Space } from 'antd';
import { useRouter } from 'next/router';

function Lesson() {
  const [close, isClosed] = useState(false);
  const back = useRouter();

  const handleClose = () => {
    isClosed(true);
  };

  return (
    <div className='container-video'>
      <div className="container-title">
        <h3>Lesson 1</h3>
        <p>Please watch the video till the end!</p>
        <button type="button" className="btn-close" aria-label="Close" onClick={handleClose}></button>
      </div>
      <div className="embed-responsive embed-responsive-16by9">
        <iframe className="embed-responsive-item" src="https://www.youtube.com/embed/zpOULjyy-n8?rel=0" title='video percobaan'></iframe>
      </div>
      <div className='overview'>
        <div className="card">
          <div className="card-body">
            <h5 className="card-title">Course Overview</h5>
            <p className="card-text">Some quick example text to build on the card title and make up the bulk of the card&apos;s content. 
            Some quick example text to build on the card title and make up the bulk of the card&apos;s content.</p>
          </div>
        </div>
        <div className="card">
          <div className="card-body">
            <Space direction='vertical'>
              <h5 className="card-title">How you rate this lesson?</h5>
              <Rate defaultValue={4} allowHalf tooltips={["Bad", "Not Really Good", "Good", "Comfort", "Best Lesson!"]}/>
            </Space>
          </div>
        </div>
        <div className="container-button d-flex justify-content-between">
          <button type="button" className="btn btn-dark" onClick={() => back.push('/freeform')}>Back Button</button>
          <button type="button" className="btn btn-warning" onClick={() => handleClose}>Next Lesson</button>
        </div>
      </div>
    </div>
  );
}

export default Lesson;`

So I face two error actually, first one was saying that the Lesson in page.tsx cannot be used as JSX component, and the second one is

Error: useRoutes() may be used only in the context of a <Router> component.

Call Stack
invariant
node_modules\.pnpm\@remix-run+router@1.7.2\node_modules\@remix-run\router\dist\router.js (232:0)
useRoutesImpl
node_modules\.pnpm\react-router@6.14.2_react@18.2.0\node_modules\react-router\dist\index.js (322:82)
useRoutes
node_modules\.pnpm\react-router@6.14.2_react@18.2.0\node_modules\react-router\dist\index.js (317:0)
Routes
node_modules\.pnpm\react-router@6.14.2_react@18.2.0\node_modules\react-router\dist\index.js (1128:0)
renderWithHooks
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (10649:0)
mountIndeterminateComponent
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (15743:0)
beginWork$1
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (17318:0)
beginWork
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25672:0)
performUnitOfWork
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (24523:0)
workLoopSync
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (24239:0)
renderRootSync
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (24204:0)
recoverFromConcurrentError
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (23450:0)
performConcurrentWorkOnRoot
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (23356:0)
workLoop
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (261:0)
flushWork
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (230:0)
MessagePort.performWorkUntilDeadline
node_modules\.pnpm\next@13.4.8_@babel+core@7.22.9_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (537:0)

How to solve this? I want to render the Aspects.Tsx, FreeForm.tsx, and also Lesson.tsx in the page.tsx, but it gives me this error. And if I changed the page.tsx to

Const Home: React.FC = () => {
    
     return (
         <main className="min-h-screen">
             <div>
                 <h1 className="text-[54px] text-blue-800">Hallo bro gua capek</h1>
    

             </div>
         </main>
     )
}

it gives the dsiplay "Hallo bro gua capek", but when I tried to render another page it does not work.


Solution

  • It's solved, I need to wrap it inside the < Browser Router> and it works fine. So do what comes inside your mind, cause I think this before but thinking too much than not doing what I was thinking the solution is.