I'm working on a project where I'm trying to combine ReactJS + Marzipano.
I'm at the stage where I've created my React project using create-react-app, installed Marzipano via npm, and copied / slightly modified some boilerplate code from a Marzipano example here to get it to fit into the React app.
Note I also installed glslify as a dependency in my project, because when I tried to import Marzipano without it, I got the following error:
Module not found: Can't resolve 'glslify'
Note also that glslify is a dev dependency for Marzipano, but not a production one. I'm thinking by installing it as a dependency may be where I tripped things up, but it was throwing the above error without it so not sure what to make of that.
Anyways what's happening now is my browser is rendering a white screen with no errors in the console, however when you click and drag the cursor changes to a closed hand, and the div that I attached Marzipano to via React's ref system has been modified, so Marzipano is definitely doing something.
Below is my App.js file (everything else is a fresh create-react-app installation):
import React, { Component } from 'react';
import './App.css';
import Marzipano from 'marzipano';
class App extends Component {
componentDidMount() {
this.panoViewer = new Marzipano.Viewer(this.pano);
// Create source.
const source = Marzipano.ImageUrlSource.fromString(
"img/test.jpg"
);
// Create geometry.
const geometry = new Marzipano.EquirectGeometry([{ width: 2048 }]);
// Create view.
const limiter = Marzipano.RectilinearView.limit.traditional(1024, 100*Math.PI/180);
const view = new Marzipano.RectilinearView({ yaw: Math.PI }, limiter);
// Create scene.
const scene = this.panoViewer.createScene({
source: source,
geometry: geometry,
view: view,
pinFirstLevel: true
});
// Display scene.
scene.switchTo();
}
render() {
return (
<div>
<div ref={pano => this.pano = pano} />
</div>
);
}
}
export default App;
The first (and only) time the render method is called, the div which you attach the ref to has no content and therefore is rendered with a height of zero.
Marzipano then creates the scene in this div, but as it reads the dimensions of the container element, it renders a canvas with zero height.
To fix this, either set a specific height on the div (for example using the style attribute) or give it a height of 100% and do the same in index.css for html, body and #root
Related
In my nextjs app, I have a StrapiImage component that accepts an image object from my strapi backend api as a prop. It assigns the width, height and url + any aditional props. (It's basically a shortcut for a regular next/image)
import Image from "next/image";
import { getStrapiMedia } from "../../lib/media";
export default function StrapiImage({ image, ...props })
return <Image src={getStrapiMedia(image)} // the getStrapiMedia function basically just returns the url to the image.
width={image.attributes.width}
height={image.attributes.height}
alt=""
{...props} />
}
Now, I wanted to add a blur placeholder for this image using placeholder="blur" but since this is an external image, I have to provided a base64 image in the blurDataUrl prop.
I wanted to generate an image like this with the plaiceholder library similarly to this sollution I found.
There's a problem with this though, to generate the image, I need to use an await statement. In getStaticProps, this wouldn't be a problem as I could just make the function async, but I'm doing this inside a component and components must regular non-async functions.
The 'obvious' sollution would be to use the useEffect hook like so:
import Image from "next/image";
import { getStrapiMedia } from "../../lib/media";
import { useEffect, useState } from "react";
import { getPlaiceholder } from "plaiceholder";
export default function StrapiImage({ image, ...props })
const [blur64, setBlur64]
useEffect(() => {
async function generatePlaceholder() {
// generate base64 image here
}
generatePlaceholder();
}, [])
return <Image src={getStrapiMedia(image)}
width={image.attributes.width}
height={image.attributes.height}
alt=""
placeholder="blur"
blurDataURL={blur64}
{...props} />
}
This would technically work, but it would only run only on the client-side, ultimately defeating the purpose of ssr and the whole image optimisation. (Especially considering plaiceholder's huge js bundle)
Is there any other way that I could do this without useEffect but still in the component file? Ideally, I'd like it to run only on the server-side.
I'm using nextjs#13
I fixed the issue by using a strapi plugin to generate the placeholder image on the backend rather than on the frontend.
The issue in the title still remains unsolved though. Running pre-renderable async code at component-level doesn't seem to be officialy supported by nextjs. (with the pages directory)
Here's some "sollutions" I've found: (mostly workarounds)
Generate the placeholders on page-level. For me, that would mean looping through all elements in my api data looking for images and generating placeholders for them. It's pretty inconvenient but it does work.
Switch to the app directory. (provided you're using next 13) It uses react server components so all of this is possible and simple to do there.
Try to work with useSSE. I haven't looked into this too much but it looks like you can use the useSSE library to run server-side at component-level. Here's an article covering this.
We have a project running with React + Material UI, now there is a request to render a certain component(use ComponentA below) by using Ionic(It's not a reasonable requirement however I only discuss from technical perspective here).
From Ionic offical documentation, I did below
install the #ionic/react and #ionic/react-router package
including below css in root component
/* Core CSS required for Ionic components to work properly */
import '#ionic/react/css/core.css';
/* Basic CSS for apps built with Ionic */
import '#ionic/react/css/normalize.css';
import '#ionic/react/css/structure.css';
import '#ionic/react/css/typography.css';
/* Optional CSS utils that can be commented out */
import '#ionic/react/css/padding.css';
import '#ionic/react/css/float-elements.css';
import '#ionic/react/css/text-alignment.css';
import '#ionic/react/css/text-transformation.css';
import '#ionic/react/css/flex-utils.css';
import '#ionic/react/css/display.css';
invoke setupIonicReact() in root component
replace material UI element with ionic element in ComponentA
But it does not work - blank page, and I have to comment out below line
import '#ionic/react/css/structure.css';
Now I can see ionic elements displayed however css styles not applied they look more like html input, button and etc.
I realise there is something wrong however I could not figure out what's the root cause, after searching a while I decide to move step 2)& 3) into that ComponentA. But, new problem coming - setupIonicReact() can not be invoked inside the component, I looked into it and it's pretty simply
export const setupIonicReact = (config: IonicConfig = {}) => {
/**
* By default Ionic Framework hides elements that
* are not hydrated, but in the CE build there is no
* hydration.
* TODO FW-2797: Remove when all integrations have been
* migrated to CE build.
*/
if (typeof (document as any) !== 'undefined') {
document.documentElement.classList.add('ion-ce');
}
initialize({
...config
});
so, I make a simple change - like below
<div className="ion-ce">
<ComponentA />
</div>
now, my chrome keeps loading and there is a blank page alway.
So, I want to stop now and ask for hep, at least to know is it possible using ionic inside a existing project with only certain component?
This is my first time making graphs in React so I'm following the guide and documentation. I have no prior experience with graphology, sigma.js or react-sigma (I know how to use normal React).
The setup documentation says:
Import the React Sigma style file in your application. Example : import "#react-sigma/core/lib/react-sigma.min.css"
What are style files? CSS? How can I make one? I can't find any documentation. Is it something basic from a previous library?
Honestly I'm finding it very hard to learn sigma.js and insert it in my react website. I can't find any guides. Are there any? I just want to start with a simple graph and learn from there.
The documentation refers specifically to their own CSS file which must be imported in a React app like they are showing in the example:
import "#react-sigma/core/lib/react-sigma.min.css";
Disclaimer: depending on your React app setup, the way to import a CSS file might differ.
The example in the documentation was broken when I wrote this answer. I opened an issue on the react-sigma repo and it was fixed since then.
When I tried it, some dependencies were missing and some changes were needed.
npm install #react-sigma/core sigma graphology graphology-types lodash
import { useEffect } from "react";
import Graph from "graphology";
import { SigmaContainer, useLoadGraph } from "#react-sigma/core";
// Import the style file here
import "#react-sigma/core/lib/react-sigma.min.css";
export const LoadGraph = () => {
// first change to their example
const loadGraph = useLoadGraph();
// also wrapped their graph instantiation side-effect with useEffect
useEffect(() => {
const graph = new Graph();
graph.addNode("first", {
// Required position
x: 1,
y: 1,
size: 15,
label: "My first node",
color: "#FA4F40"
});
// Calling the function that was missing from the example
loadGraph(graph);
}, [loadGraph]);
// Returning null to get a valid component
return null;
};
export const DisplayGraph = () => {
return (
<SigmaContainer style={{ height: "500px", width: "500px" }}>
<LoadGraph />
</SigmaContainer>
);
};
Note that I used TypeScript for the example, which gives insightful error messages since react-sigma provides its own types.
Then it was clear that the useLoadGraph hook wasn't used properly since it doesn't accept any parameter and it returns a function accepting a graph parameter. This can be confirmed with the API documentation.
I also figured that lodash was missing from errors in the developer console.
Please refer to the documentation as it's now up-to-date.
I am talking about a big web app that has jquery and bootstrap stuff init.
Part of rewriting this giant app using react and material UI, we are writing component by component. Things work fine in general as we make progress toward making this a react app soon.
Our problem:
When a new react component is loaded into the page, the existing (already loaded) react components will lose some or all styles.
We checked the new component load new style (classes) which are matching the names of existing classes for other already loaded components.
Ex:
As you can see, jss1, jss2, ... MuiCardHeader, MuiGrid, ... were also the names for the previously loaded components and now they are overwritten for the newly loaded component.
Packages.json:
webpack config:
Some component code: we are using make style and in some cases withstyle
Tried too much stuff. But nothing seems to work. On initial load, the map has all the correct material-ui stuff but as soon as I click on a marker and the popup component loads in. Now the map is messed up as some styles are overwritten.
How can we make it so that each component styles are named unique and never conflicts with other stuff?
I checked MUI docs and GitHub issues about kinda similar issue but nothing is working for us.
Any thoughts?
If I add:
import { StylesProvider, createGenerateClassName } from '#material-ui/core/styles';
const generateClassName1 = createGenerateClassName({
seed: 'App2',
});
my custom classes ( like root, mydivstyle) will have the prefix like app2-jss1-root, app2-jss2-mydivstyle, ...
but muiCard, MuiCardHeader, ... still being overwritten.
So after a few trial and error. I was able to solve this.
Basically we don't have a component tree path where you can could add a single class generator because of our app structure at this stage. We will soon have one.
The solution was to wrap every component like.
import { StylesProvider, createGenerateClassName } from '#material-ui/core/styles';
const generateClassName = createGenerateClassName({
seed: 'anyprefix',
});
class ActivityContainer extends Component {
render() {
return (
<StylesProvider generateClassName={generateClassName}>
<componenet/>
</StylesProvider>
);
};
}
I had to do this on the container level, this way any component loaded in that container will inherit this prefix.
No more conflicting class names between components and can load them dynamically.
Not a great way but no other option at least in our case. It will leave more classes on the DOM.
I am new to React server side rendering , working on a small demo with React , Redux , React router , Material UI. The problem I am facing is the below warning. Unsure how the isomorphic styling and assets works with webpack.
I understood the pipeline how server side rendering work , please correct me if it is wrong.
Using renderToString the React component get resolved to the HTML.
Once the HTML renders on the client side , all the events , styling gets attached meaning react try to render the component on the client side again , if the component is already created it will create it again.
If the component is already created or NOT is derived based on the Checksum.
Issue reported in GIT
https://github.com/callemall/material-ui/issues/4466
Code
https://github.com/sabha/React-Redux-Router-MaterialUI-ServerSideRendering
'warning.js:44Warning: React attempted to reuse markup in a container
but the checksum was invalid. This generally means that you are using
server rendering and the markup generated on the server was not what
the client was expecting. React injected new markup to compensate
which works but you have lost many of the benefits of server
rendering. Instead, figure out why the markup being generated is
different on the client or server: (client)
0;text-align:center;mui-prepared:;-webki (server)
0;text-align:center;-webkit-user-select:'
In this Razzle Material UI Styled Example project I am setting the user agent this way:
server.js:
renderToString(<Application userAgent={request.headers['user-agent']} />)
client.js:
hydrate(<Application userAgent={navigator.userAgent} />, document.getElementById('root'))
Main.js:
class Main extends Component {
constructor(properties, context) {
super(properties, context)
this.muiTheme = getMuiTheme({
userAgent: properties.userAgent
})
}
render() {
return (
<MuiThemeProvider muiTheme={this.muiTheme}></MuiThemeProvider>
)
}
}
It works well and I think it is correct too.
Material-UI, with its inline-style approach to styling elements, has this gotcha with respect to server-side rendering:
On the client, MaterialUI rendering only adds inline styles specific to the browser that is running the app. But on the server, you aren't in a browser and so how to know which specific style rules to use so that the rendered HTML matches the client-rendered HTML and avoids the React warning?
They have documentation on how to solve the issue. Essentially it means ensuring you set the user agent string (captured from the HTTP Request Header) before doing your server side rendering.
This is from their serverside rendering documentation:
import getMuiTheme from 'material-ui/getMuiTheme';
import MuiThemeProvider from 'material-ui/MuiThemeProvider';
import {green100, green500, green700} from 'material-ui/styles/colors';
const muiTheme = getMuiTheme({
palette: {
primary1Color: green500,
primary2Color: green700,
primary3Color: green100,
},
}, {
avatar: {
borderColor: null,
},
userAgent: req.headers['user-agent'],
});
class Main extends React.Component {
render() {
return (
<MuiThemeProvider muiTheme={muiTheme}>
<div>Hello world</div>
</MuiThemeProvider>
);
}
}
(client) 0;text-align:center;mui-prepared:;-webki
(server) 0;text-align:center;-webkit-user-select:
You will notice that the difference between the two is extra mui-prepared that is not present in the server render. There is a note about this at the bottom of the server rendering guide in the docs.
In order to make sure our style transformations are only applied once, we add an additional property to each style when process.env.NODE_ENV !== 'production'.
It looks like you have process.env.NODE_ENV=production only on the server (this was the case for me).