Loading gltf in gatsby site with react-three-fiber - reactjs

I've been trying to load a GLTF model into my gatsby site using react-three-fiber, but can't seem to get it to load. This seems like it should be very simple, but I'm new to Gatsby and threejs and was wondering if I could get some guidance.
My model is stored as static/models/crerar.glb, and I used gltfjsx to generate a Model component. I've tried referencing just 'models/crerar.glb' but haven't had luck either.
In index.js, I have:
import Layout from "../components/layout"
import SEO from "../components/seo"
import React, { Suspense, useRef, useState } from "react"
import { Canvas, useFrame, useLoader } from "react-three-fiber"
import Model from "../components/Crerar"
const IndexPage = () => (
<Layout>
<Canvas>
<ambientLight intensity={0.2} />
<Model />
</Canvas>
</Layout>
)
export default IndexPage
and in Crerar.js (stored in components)
/*
auto-generated by: https://github.com/react-spring/gltfjsx
*/
import * as THREE from 'three'
import React, { useRef } from 'react'
import { useLoader } from 'react-three-fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
export default function Model(props) {
const group = useRef()
const { nodes, materials } = useLoader(GLTFLoader, '../static/models/crerar.glb')
return (
<group ref={group} {...props} dispose={null}>
<mesh material={nodes.mesh_0.material} geometry={nodes.mesh_0.geometry} />
</group>
)
}

the path is wrong. the json.parse error you're getting is because the loader tries to parse a HTML 404 fetch-error as a GLTF. you can make sure by opening dev tools and the networking tab. you should see it's trying to reach your file, but can't.
if the model is within your src folder you have to import it first, then use the hashed url that you get. this is my recommendation, don't mess around with public files, always import your stuff. it's safer, the compiler will complain if the file isn't present, and it's better for cache control.
otherwise, if the file is inside /public or i guess it's /static in gatsby (?) then the url has to be something like "/file.glb". sometimes it's /public/file.glb or /static/file.glb, it depends on your bundling environment (you can try fetching the file via the browsers url bar, if an url works, that's the one you should pass on to the loader).
if your file is draco compressed, then draco must also be inside public or static. see: https://codesandbox.io/s/r3f-ibl-envmap-simple-y541h
you can safely use useLoader(GLTFLoader, url), it's just a wrapper around new GLTFLoader().load(url, data => ...) + suspense. It's not experimental any longer, even though it may have that warning on Github.

gatsby copies everything from static into the public folder, so change your url to:
const { nodes, materials } = useLoader(GLTFLoader, '/models/crerar.glb')

Related

What happens if two components import the same CSS file in React?

Let's say I have two React components:
import { React } from "react";
import "./styles.css";
function ComponentA() {
...
}
export default ComponentA;
import { React } from "react";
import "./styles.css";
function ComponentB() {
...
}
export default ComponentB;
Both of these components are importing the same CSS file styles.css. Now let's say that in my app, I'm importing both of these components, so App.js looks something like this:
import { ComponentA } from "./ComponentA";
import { ComponentB } from "./ComponentB";
function App() {
return (
<div className="App">
<ComponentA />
<ComponentB />
</div>
);
}
export default App;
What exactly happens here? Does my app import the same CSS file twice? And if so, do I just make the import on the App.js file instead?
It's as if it was only imported once.
When a module bundler like Webpack sees an import, it puts the following path in the list of files to process, if the path doesn't exist already. The file is only processed once, no matter how many times it's imported.
Note that with React apps, you will often see
import React from "react";
in tens or hundreds of files - React isn't created anew hundreds of times. Rather, the module is processed once, and then files that import it are given access to what React has exported.
Importing CSS files works the same way, in that they're only processed once no matter how many times an import exists for them (though they don't really have exports, just side-effects).
If both ComponentA and ComponentB depend on styles.css, feel free to keep importing styles.css in both - it doesn't hurt, and will make things easier to manage when you can see at a glance that both components directly depend on that CSS.

'Component exception' React Native

I copied my code from an old project of mine so it should be able to work. However, I think when I made the new project it accidentally updated the versions causing it to go thorough an error. This is my code:
import React, {useState} from 'react';
import { Text, View } from 'react-native';
import * as Font from 'expo-font';
import { AppLoading} from 'expo-app-loading';
import {enableScreens} from 'react-native-screens'
//import from libraries
import TabNavigation from './Navigation';
//import TabNavigation
enableScreens();
const fetchFonts = () => {
return Font.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold' : require('./assets/fonts/OpenSans-Bold.ttf')
});
//fetch fonts from the folder so that we can style the title
};
export default function App() {
const[fontLoaded, setFontLoaded] = useState(false);
if(!fontLoaded){
return (
<AppLoading
startAsync ={fetchFonts}
onFinish={()=> setFontLoaded(true)}
/>
);
}
//this will ensure that the font will always be loaded.
return(
<TabNavigation />
);
}
The thing I'm trying to do is run this program and then return Navigation where all the navigation process works.
Here is the error:
Component exception
Element type is invalid: expected a string(for built-in components) or a class/function (for complete components) but got: undefined. You likely forgot to export your component from. the file it's defined in, or you might have mixed up a default and named imports. Check the render method of 'App'.
You don't need to do this fetchFonts method
to load the fonts. If you installed the fonts correctly you just need to put the attribute fontFamily in any style, with the name of the font as its value, for example: textStyle:{ fontFamily: 'Montserrat-Bold' }
For a better understanding you can check this link.
Change your import for app Loading
import AppLoading from 'expo-app-loading';
First, open the terminal and go to the project directory.
Second, run the following command to install the expo-app-loading module:
expo-app-loading
Third, import the module into your App.js file:
import AppLoading from 'expo-app-loading';
Tested on Expo 4.9.0

How to use dynamic import with a named export in Next.js?

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

React preloading components with Lazy + Suspense

I'm currently using React 16 with Suspense and Lazy to code-split my codebase. Although I would like to preload components.
In my example below I got two routes. Is there a way to preload Demo as soon Prime did mount? I've tried to create another dynamic import in the componentDidMount of the Prime page, but React.lazy doesn't seem to get that that's the same file as the dynamic import below.
import React, { lazy, Suspense } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import GlobalStyle from 'styles';
import Loading from 'common/Loading';
const Prime = lazy(() => import(/* webpackChunkName: "Prime" */'modules/Prime'));
const Demo = lazy(() => import(/* webpackChunkName: "Demo" */'modules/Demo'));
const App = () => (
<main>
<GlobalStyle />
<Suspense fallback={<Loading>Loading...</Loading>}>
<Switch>
<Route path="/" component={Prime} exact />
<Route path="/demo" component={Demo} />
</Switch>
</Suspense>
</main>
);
export default withRouter(App);
So I've tried different approaches, for example with and without webpackChunkName and different ways of importing the other component in componentDidMount, as seen below. The first two approaches of importing the file in componentDidMount resulted in a Webpack error shown at the bottom of the image below. Only the third proceeded, but made the file 2.[hash].js in the image, only load after the page was visited, and not on componentDidMount
What am I missing here?
Code of modules/Demo.jsx:
import React from 'react';
import LogoIcon from 'vectors/logo.svg';
import PageLink from 'common/PageLink';
import Anchor from 'common/Anchor';
import CenteredSection from 'common/CenteredSection';
const Demo = () => (
<CenteredSection variant="green">
<LogoIcon />
<PageLink to="/" variant="green">Go to home page</PageLink>
</CenteredSection>
);
export default Demo;
This is incredibly easy to do, I think there is a misunderstanding about what lazy() and Suspense are doing under the hood.
The only expectation that React.lazy() has is that it takes a function that returns a Promise that resolves with a default component.
React.lazy(() => Promise<{default: MyComponent}>)
So if you want to preload, all you have to do is execute the promise yourself ahead of time.
// So change this, which will NOT preload
import React from 'react';
const MyLazyComp = React.lazy(() => import('./path/to/component'));
/*********************************************/
// To this, which WILL preload
import React from 'react';
// kicks off immediately when the current file is imported
const componentPromise = import('./path/to/component');
// by the time this gets rendered, your component is probably already loaded
// Suspense still works exactly the same with this.
const MyLazyComp = React.lazy(() => componentPromise);
The fact that this is a known signature makes it for useful for all sorts of other situations. For instance, I have a bunch of components that rely on the google maps api being dynamically loaded, I was able to create a function that loads the google maps api and then imports the component. I won't detail the internals of this example since it's a tangent, but the point is I made myself a function that does a bunch of async stuff and then returns a Promise with an object of {default: Component}.
import React from 'react';
const MyLazyComp = React.lazy(() => importMapsComponent('./path/to/comp'));
Not sure how much help this will be, but here is a code sandbox that works (Demo gets loaded by componentDidMount). It is a considerably simplified version of your code using create-react-app for the config. Perhaps you can take this as a starting point and morph it gradually closer to your app to see what causes the dynamic import to no longer work as desired.

error in displaying pdf in react-pdf

I am new new to react, I am trying to display a pdf file on browser. I am getting an error as failed to load PDF. I am trying to run the sample program given in https://www.npmjs.com/package/react-pdf.
App.js
import React, { Component } from 'react';
import { Document, Page } from 'react-pdf';
class MyApp extends Component {
state = {
numPages: null,
pageNumber: 1,
}
onDocumentLoad = ({ numPages }) => {
this.setState({ numPages });
}
render() {
const { pageNumber, numPages } = this.state;
return (
<div>
<Document
file="./1.pdf"
onLoadSuccess={this.onDocumentLoad}
>
<Page pageNumber={pageNumber} />
</Document>
</div>
);
}
}
export default MyApp;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
Error screenshot
Question is old but hope this help someone. I faced this issue and found a solution here https://github.com/wojtekmaj/react-pdf/issues/321.
import { Document, Page, pdfjs } from 'react-pdf';
Add this to your constructor.
constructor(props){
super(props);
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
}
You load the file using file="./1.pdf" I believe that might be the problem.
If you have a file structure like:
src
App.js
components
ShowPdfComponent.js
1.pdf
public
bundle.js
Then you need to move the 1.pdf to public folder like this:
src
App.js
components
ShowPdfComponent.js
public
bundle.js
1.pdf
Because when your compiled javascript code is being executed from public/bundle.js and bundle.js does not know how to get to src/components/1.pdf in file system.
There might be also a difference between production/development environment if you are using webpack and webpack-dev-server.
Look at react-pdf example. It has flat file structure. That is the reason why it works.
if you are using create-react-app then you need to import differently like
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack'
because it uses webpack under the hood.
You can import the pdf file using import samplePdf from './1.pdf' and can use directly like file={samplePdf} in your Document tag.
Here is the minimum setup you need to be able to display your pdf in react TypeScript:
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist#${pdfjs.version}/legacy/build/pdf.worker.min.js`;
const YourComponentReact = ({url}: {url: string}) => {
return (<Document file={url}>
<Page pageNumber={1} />
</Document>)
}
You can add a quick button if you want to be able to change pages
You only need to download react-pdf and #types/react-pdf if you use typescript.
adding pdfjs to import and using it like this hellped
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist#${pdfjs.version}/legacy/build/pdf.worker.min.js`;
in the same file i need to show my pdf

Resources