kylewetton / deviceful

Interactive, animatable devices to digital projects
26 stars 0 forks source link

Can't use this with a React (NextJS) application #4

Closed alansiq closed 3 years ago

alansiq commented 3 years ago

Hey, could anyone help me to use this on a React App?

This is my first time using a non-react library into react and, to be honest, I'm quite lost over here.

import Deviceful from 'deviceful';

const LaptopDeviceful = () => {
    const laptop = new Deviceful({
        parent: '#laptop_deviceful',
        device: "laptop",
        screenshot: "./public/redgarden.jpg",
    })

    return (
        <>
            <div id="laptop_deviceful">

            </div>
            {laptop.mount()}
        </>
    )
}

export default LaptopDeviceful;

This is the error I get: image

Any help is appreciated!

kylewetton commented 3 years ago

Hey @alansiq, I've updated Deviceful to v0.9.4.

This includes two things which will make Deviceful work in React for you. The first being the ability to pass the parent to the mount method inside a useEffect hook, the second being the ability to change the path.

Both of these are used in this example: https://github.com/kylewetton/deviceful-starter-react

Check out the App.js file to see how Deviceful would be set up inside a component. Let me know if this solves it for you.

alansiq commented 3 years ago

I'll give it a try on a fresh React project as, of right now, I couldn't get it running either. Still got the same: "Cannot use import statement outside a module" error as shown above.

Here's my component:

import React, { useEffect, useRef } from "react";
import Deviceful from "deviceful";

const LaptopDeviceful = () => {

    const parent = useRef();
    const laptop = new Deviceful({
        parent: parent,
        device: "laptop",
        screenshotHeight: 2402,
        screenshot: "./public/redgarden.jpg",
        path: "./deviceful",
    })

    useEffect(() => {
        laptop.mount(parent.current);
    }, []);

    return (
        <>
             <div ref={parent} className="laptop"></div>
        </>
    )
}

export default LaptopDeviceful;

Here's my package.json:

{
  "name": "nextjs",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "deviceful": "^0.9.4",
    "next": "9.5.1",
    "react": "16.13.1",
    "react-bodymovin": "^2.0.0",
    "react-dom": "16.13.1",
    "react-icons": "^3.10.0",
    "react-lottie": "^1.2.3",
    "react-tsparticles": "^1.17.7",
    "react-typical": "^0.1.3",
    "sass": "^1.26.10",
    "webpack": "^4.44.1"
  },
  "devDependencies": {
    "@types/node": "^14.0.27",
    "@types/react": "^16.9.46",
    "@types/react-lottie": "^1.2.5",
    "typescript": "^3.9.7"
  }
}
kylewetton commented 3 years ago

Hey @alansiq, a fresh React project and a fresh Next.js project aren't exactly the same thing. The import seems to be trying to run on the server. I don't have experience with Next.js but this problem is unique to Next.js, here's a few leads that might clear up whats going on

https://github.com/vercel/next.js/issues/5514 https://github.com/vercel/next.js/issues/9890

I'll test out Deviceful with Next.js when I get a chance, if you figure it out in the mean time, let me know

alansiq commented 3 years ago

I'm sorry for wasting your time - I'm quite a beginner and didn't figure it out by myself.

The issue was that NextJS was trying to render this component server side. All I had to do was disable SSR for Deviceful.

That was simple:

This is my component in root nextjs folder: /components

function App() {

    const parent = useRef();
    const device = new Deviceful({
      screenshot: "redgarden.jpg",
      screenshotHeight: 2402,
      path: "./deviceful",
    });

    useEffect(() => {
      device.mount(parent.current);
    }, []);

    return (
      <>
        <div ref={parent} className={styles.laptop}>

        </div>

        <div className={styles.flex}>
          <button onClick={() => device.swivel()}>Swivel</button>
          <button onClick={() => device.swivel({ to: 0 })}>Center</button>
        </div>
      </>
    );

}

export default App;

Now, in the page I want to import this component:

import dynamic from 'next/dynamic';

const Deviceful = dynamic(() => import('../../components/deviceful'), {
    ssr: false
})

function Page() {
    return <Deviceful />
}

export default Page;

I love Deviceful and I'll send my support your way in the next 24 hours. Thank you for this awesome library and anyone in the future that needs help setting it up with NextJS - you're more than welcome to reach out to me.

edit: By the way - since Next "dynamic" works somehow like an async function, you'll have to set the width for the parent element aswell - else it'll be 0.

kylewetton commented 3 years ago

No worries at all, best of luck with your project!