React Component not working properly with Mapbox - reactjs

I'm new on React, but due to requirements at an assigment in college, I must use it . I must do a single page application (SPA), with various components. I used npx create-react-app to setup the base project, and so you have an idea, it looks like this right now:
Each functionality such as "create new node", "create new line", etc, is an individual component.
On my App.js, I'm setting a state for each component as false, only making it true when the key of the submenu associated with that specific component is clicked, allowing me to switch between the sidebar options with no problem, and then at each component, I have display: false at the state and also this:
componentWillUpdate(nextprops) {
if (nextprops.display !== this.props.display) {
this.setState({ display: nextprops.display });
}
}
so it makes that display true, so that component is loaded. Everything works nicely, only the map viewer has a problem where the map by mapboxgl displays in full screen, covering the sidebar, not allowing me to change between components. The MapViewer component code is:
import React, { Component } from "react";
import mapboxgl from "mapbox-gl";
//import 'mapbox-gl/dist/mapbox-gl.css'
mapboxgl.accessToken="MY_KEY_IS_HERE";
class MapViewerComp extends Component {
constructor(props) {
super(props);
this.state = {
longitude: -8.562298,
latitude: 41.187181,
zoom: 12,
display: false,
};
}
componentWillUpdate(nextprops) {
if (nextprops.display !== this.props.display) {
this.setState({ display: nextprops.display });
}
}
displayTheMap = () => {
const map = new mapboxgl.Map({
container: "root",
style: "mapbox://styles/mapbox/streets-v11",
center: [this.state.longitude, this.state.latitude],
zoom: this.state.zoom
});
var marker = new mapboxgl.Marker().setLngLat([-8.562298, 41.187181]).addTo(map);
};
render() {
let pageContent = <div></div>;
if (this.state.display) {
pageContent = (
<div>
<div ref={this.displayTheMap} className="mapContainer"/>
</div>
);
}
return <div>{pageContent}</div>;
}
}
export default MapViewerComp;
And the .css looks like this:
.ant-switch-handle {
text-align: right;
}
.mapContainer {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
When I click the "Map Viewer" option on my sidebar (which corresponds to my (MapViewerComp), it pops out like this:
Since I'm new to React and frontend, the only was I was able to force it to fit my screen like this:
was to add this piece to the .css of this component:
.mapboxgl-canvas{
position: absolute;
left: 271px;
top: 65px;
}
The 2 problems are:
1st: if I force it to the size I want like I showed you, now when I zoom in and out on the map, the marker moves around and doesn't stay in place.
2nd: when I click the other options on the sidebar, to switch between functionalities, it doesn't work, it keeps showing the map.
The code of the index.js at src(with the npx create-react-app structure) is:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/app/App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
And the code for the index.html on public folder is:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="Web site created using create-react-app"/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png"/>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json"/>
<script src="../src/components/mapViewerComp/MapViewerComp.js"></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v1.13.0/mapbox-gl.css' rel='stylesheet' />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
here's the structure of the project as well if it helps in any way:
Any fix to any of the issues would be appreciated. All other components work like a charm, yet the map viewer is not...
Thank you in advance.

Edit: Turns out the mapbox is set to render inside of the app root container, which why it is taking all the available space and not going away when re-rendering the app. Setting margins exposes the sidebar and allows to interact with it but alas doesn't trigger the map to go away.
See the doc here.

Related

To publish Unity default AR Project with React Unity WebGL has a different serialization error

Development Environment
Macbook M1 Pro (With Apple Silicon Chip)
Unity 2021.3.10f1 (Be compatible with Apple Silicon Chip)
Test with Chrome Browser
Current Progress
I just create a new project with AR template
The project's hierarchy is like below picture.
That building Setting is like below picture.
After building, I get 4 files named build.data, build.framework.js, build.loader.js, build.wasm
With a create-react-app command, I created a new react project. (npx create-react-app ar_tutorial_4 --template typescript)
I located those 4 build files under the react's build directory. Like below image.
I edited index.html file like this.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React Unity webGL</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
And, I edited App.tsx file and index.tsx file like this.
// App.tsx
import { useRef } from "react";
import { Unity, useUnityContext } from "react-unity-webgl";
function UnityTest() {
const {
unityProvider,
isLoaded,
loadingProgression,
} = useUnityContext({
codeUrl: '/build/build.wasm',
dataUrl: '/build/build.data',
frameworkUrl: '/build/build.framework.js',
loaderUrl: '/build/build.loader.js',
webglContextAttributes: {
preserveDrawingBuffer: true,
},
});
const loadingPercentage = Math.round(loadingProgression * 100);
const canvasRef = useRef<HTMLCanvasElement>(null);
return (
<div className="container">
{isLoaded === false && (
<div className="loading-overlay">
<p>Loading... ({loadingPercentage}%)</p>
</div>
)}
<Unity
className="unity"
unityProvider={unityProvider}
style={{ border: "1px solid red", height: 400, width: 500 }}
devicePixelRatio={window.devicePixelRatio}
ref={canvasRef}
/>
</div>
);
}
export { UnityTest };
// index.tsx
import React, { createElement } from 'react';
import ReactDOM from 'react-dom/client';
import { UnityTest } from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(createElement(UnityTest));
Problem
The screen was normally disappeared but the unity container did not played well.
And, this console error message was displayed.
build.framework.js:1771 A scripted object (script unknown or not yet loaded) has a different serialization layout when loading. (Read 52 bytes but expected 76 bytes)
Did you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts?
Question
What should I do to resolve this problem??

FontAwesome 5 icons not showing in React App

I'm using FontAwesome inside a React App and TypesScript(created with react-react-app), but the icons aren't showing yet I followed the documentation's guide step-by-step.
Importing icons globally (index.tsx)
import { library } from '#fortawesome/fontawesome-svg-core';
import { faCheckSquare, faCoffee } from '#fortawesome/free-solid-svg-icons';
library.add(faCheckSquare, faCoffee);
Using the icons in my component
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
export const Home = ()=> {
return <FontAwesomeIcon icon='coffee' size='lg'/>
}
What is happening?
The below screenshot shows where the icon is supposed to be next to view price changes but it's not visible. What am I doing wrong? Thanks in advance.
Most probably Css of font-awesome is not added
try adding this in your index.html file.
<head>
<meta charset="UTF-8">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-
awesome.min.css" rel="stylesheet" integrity="sha384-
wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
crossorigin="anonymous">
</head>

React diagram invisible and shows error at <DefaultLinkModel>

I tried using react-diagrams library and I'm facing some problems.
Firstly at the line
const link = port1.link<DefaultLinkModel>(port2);
it shows me a error that
TypeError: link.addLabel is not a function
and suppose I remove the linking part, the code runs, I get no errors but nothing is seen on the webpage...
here is the code
App.js
import React from "react";
import createEngine, {
DefaultLinkModel,
DefaultNodeModel,
DiagramModel
} from "#projectstorm/react-diagrams";
import { CanvasWidget } from "#projectstorm/react-canvas-core";
// create an instance of the engine with all the defaults
const engine = createEngine();
// node 1
const node1 = new DefaultNodeModel({
name: "Node 1",
color: "rgb(0,192,255)"
});
node1.setPosition(100, 100);
let port1 = node1.addOutPort("Out");
// node 2
const node2 = new DefaultNodeModel({
name: "Node 1",
color: "rgb(0,192,255)"
});
node2.setPosition(100, 100);
let port2 = node2.addOutPort("Out");
// link them and add a label to the link
const link = port1.link < DefaultLinkModel > port2;
link.addLabel("Hello World!");
const model = new DiagramModel();
model.addAll(node1, node2);
engine.setModel(model);
class App extends React.Component {
render() {
return <CanvasWidget engine={engine} />;
}
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from "./App"
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<title>React App</title>
</head>
<body>
<style>
.srd-diagram {
height: 100vh;
}
</style>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
pls, help me sort this...
I know this post is too old, but still can be helpful if someone is struggling.
If you want to add the link between the nodes, try setting the source and target port for the link.
const node1 = new DefaultNodeModel({
name: "Node 1",
color: "rgb(0,192,255)",
});
node1.setPosition(100, 100);
let port1 = node1.addOutPort("Out");
const node2 = new DefaultNodeModel({
name: "Node 2",
color: "rgb(0,100,255)",
});
node2.setPosition(100, 200);
let port2 = node2.addInPort("In");
const link = new DefaultLinkModel();
link.setSourcePort(port1);
link.setTargetPort(port2);
For the second part:
and suppose I remove the linking part, the code runs, I get no errors but nothing is seen on the webpage...
You need to set the canvas height and width to make it visible.
App.js
<CanvasWidget className="canvas" engine={engine} />
App.css
.canvas {
height: 400px;
width: 100%;
}

Botframework with Reactjs is not working on IE11

botframework with react is not working in IE,
I'm using my index html file similar to
https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/03.a.host-with-react, its working in chrome but not in IE, i tried using webchat-es5.js also.
I'm using token given by bot team.
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Web Chat: Integrate with React</title>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#16.5.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.5.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-redux#5.0.7/dist/react-redux.min.js"></script>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat-es5.js"></script>
<style>
html, body { height: 100% }
body { margin: 0 }
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<div id="webchat" role="main"></div>
<script type="text/babel">
function start() {
const { createStore, ReactWebChat } = window.WebChat;
const { Provider } = window.ReactRedux;
const store = createStore();
window.ReactDOM.render(
<Provider store={ store }>
<ReactWebChat
directLine={ window.WebChat.createDirectLine({ token:'mytoken' }) }
storeKey="webchat"
/>
</Provider>,
document.getElementById('webchat')
);
document.querySelector('#webchat > *').focus();
}
start();
</script>
</body>
</html>
working in Chrome but not in IE, somebody help me on this please.
Unfortunatley, the "webchat-es5.js" package was designed for instantiating web chat via the window.WebChat.renderWebChat method. While the "webchat.js" package does allow for using window.ReactDOM.render, it is not designed for older browsers, such as IE.
I played with this a bunch and was simply unable to render web chat using window.ReactDOM.render while also in IE, despite utilizing any number of polyfills. That being said, I was able to get the hosted React web chat sample to work in a proper React project with a few of modifications. Please note, this also makes use of webpack.
Hope of help!
index.js: Nothing special or unexpected here.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import './css/index.css';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
app.js: Just some necessary routing.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import WebChatView from './webChatView';
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Route path="/" exact component={WebChatView} />
</div>
</Router>
);
}
}
export default App;
webChatView.js: Renders the web chat page (with some necessary styling)
import React, { Component } from 'react';
import WebChatView from './webchatView';
const FragmentStyling = {
'container': {
'display': 'flex',
'justifyContent': 'center'
},
'div': {
'height': '40rem',
'minHeight': '20rem',
'width': '1200px',
'display': 'flex',
'justifyContent': 'space-around',
'marginTop': '2rem'
}
}
class WebChatView extends Component {
render() {
return (
<div style={FragmentStyling.container}>
<div style={FragmentStyling.div}>
<WebChatView id="webchat" role="main" />
</div >
</div>
)
}
}
export default WebChatView;
webchat.js: Several things to note:
Either import '#babel/polyfill' needs to be included or all the 'core-js' imports listed below. Babel recommends importing only the required polyfills (to keep package size down). Those shown below are what is needed. However, using the '#babel' option works, as well.
Simply using fetch as-is breaks due to compatibility issues. There may be other options, but the below 'whatwg-fetch' option works in both IE and Chrome. Others I tested did not.
'fetching' the token needs to be promise-based. Using async/await breaks React web chat in IE.
import 'core-js/es6/map';
import 'core-js/es6/promise'
import 'core-js/es6/set';
import 'core-js/es6/symbol';
import 'core-js/fn/array/find-index';
import 'core-js/fn/array/includes';
import 'core-js/fn/math/sign';
import 'core-js/fn/object/assign';
import 'core-js/fn/string/starts-with';
import { fetch as fetchPolyfill } from 'whatwg-fetch';
import React from 'react';
import ReactWebChat, { createDirectLine } from 'botframework-webchat';
export default class WebChat extends React.Component {
constructor(props) {
super(props);
this.state = {
directLine: null
};
}
componentDidMount() {
this.fetchToken();
}
fetchToken(token) {
fetchPolyfill('http://localhost:3500/directline/token', { method: 'POST' })
.then(res => res.json()) // expecting a json response
.then(json =>
this.setState(() => ({
directLine: createDirectLine(
{
token: json.token
}
)
}))
)
}
render() {
return (
this.state.directLine ?
<ReactWebChat
directLine={this.state.directLine}
/>
:
<div>Connecting to bot…</div>
)
}
}
index.html: The 'react-polyfill.min.js' package needs to be included and should precede any other scripts that might live here.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//pitzcarraldo.github.io/react-polyfill/react-polyfill.min.js" charSet="UTF-8"></script>
<title>React Web App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
webpack.config.js: The import 'script!react-polyfill' needs to be included at the top of this file.
import 'script!react-polyfill';
const path = require('path');
module.exports = {
entry: ['./src/index.js'],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
mode: 'development'
};

Load Google Place API in Gatsbyjs (Reactjs) project

I am trying to use the AutoComplete address service from Google Place API.
Found this library:
https://github.com/kenny-hibino/react-places-autocomplete#load-google-library
It asks for loading the library in my project:
https://github.com/kenny-hibino/react-places-autocomplete#getting-started
I would do it in the public/index.html if it's pure Reactjs project. However, the public/index.html in Gatsbyjs project will be deleted and re-generated every time when running:
Gatsby develop
command line.
How can I use the Google Place API in my Gatsbyjs project?
Update
I have tried 2 ways to achieve this.
Use React-Helmet in /layouts/index.js , here is how it looks like:
<Helmet>
<script src="https://maps.googleapis.com/maps/api/js?key={API}&libraries=places&callback=initAutocomplete" async defer></script>
</Helmet>
Put the script reference in the /public/index.html, which looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title data-react-helmet="true"></title>
<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key={API_KEY}&libraries=places" async defer ></script>
</head>
<body>
<div id="___gatsby"></div>
<script src="/commons.js"></script>
</body>
</html>
For the 1st solution, every time after I refresh my page, the project throws an error asking for loading the Google JavaScript Map API.
For the 2nd solution, every time after I re-start the Gatsby by the command line: gatsby develop
it re-generates the index.html which flushes away my JavaScript reference in it.
You shouldn't modify any files in the public forlder with GatsbyJS.
Instead, I recommend you to customize your html.js file.
To do so, first run:
cp .cache/default-html.js src/html.js
You should have the html.js file in /src/html.js.
Now you can put your <script> tag within the <head>.
Update Feb 24, 2020
Here's a more modern implementation using React hooks with some performance optimizations based on React.memo and a custom shouldUpdate function. See this blog post for details.
import { functions, isEqual, omit } from 'lodash'
import React, { useState, useEffect, useRef } from 'react'
function Map({ options, onMount, className, onMountProps }) {
const ref = useRef()
const [map, setMap] = useState()
useEffect(() => {
// The Map constructor modifies its options object in place by adding
// a mapTypeId with default value 'roadmap'. This confuses shouldNotUpdate.
// { ...options } prevents this by passing in a copy.
const onLoad = () =>
setMap(new window.google.maps.Map(ref.current, { ...options }))
if (!window.google) {
const script = document.createElement(`script`)
script.src = `https://maps.googleapis.com/maps/api/js?key=` + YOUR_API_KEY
document.head.append(script)
script.addEventListener(`load`, onLoad)
return () => script.removeEventListener(`load`, onLoad)
} else onLoad()
}, [options])
if (map && typeof onMount === `function`) onMount(map, onMountProps)
return (
<div
style={{ height: `60vh`, margin: ` 1em 0`, borderRadius: ` 0.5em` }}
{...{ ref, className }}
/>
)
}
function shouldNotUpdate(props, nextProps) {
const [funcs, nextFuncs] = [functions(props), functions(nextProps)]
const noPropChange = isEqual(omit(props, funcs), omit(nextProps, nextFuncs))
const noFuncChange =
funcs.length === nextFuncs.length &&
funcs.every(fn => props[fn].toString() === nextProps[fn].toString())
return noPropChange && noFuncChange
}
export default React.memo(Map, shouldNotUpdate)
Map.defaultProps = {
options: {
center: { lat: 48, lng: 8 },
zoom: 5,
},
}
Old Answer
Using html.js
Modifying src/html.js like so (as Nenu suggests) is one option.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class HTML extends Component {
render() {
return (
<html {...this.props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
{this.props.headComponents}
</head>
<body {...this.props.bodyAttributes}>
{this.props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: this.props.body }}
/>
{this.props.postBodyComponents}
// MODIFICATION // ===================
<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"
async
defer
/>
// ===================
</body>
</html>
)
}
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
Then you can access the Google Maps API anywhere in your project from window.google.maps.(Map|Marker|etc.).
The React way
To me that felt a little anachronistic, though. If you want a reusable React component that you can import into any page or template as import Map from './Map', I suggest this instead. (Hint: See update below for equivalent function component.)
// src/components/Map.js
import React, { Component } from 'react'
export default class Map extends Component {
onLoad = () => {
const map = new window.google.maps.Map(
document.getElementById(this.props.id),
this.props.options
)
this.props.onMount(map)
}
componentDidMount() {
if (!window.google) {
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = `https://maps.google.com/maps/api/js?key=YOUR_API_KEY`
const headScript = document.getElementsByTagName('script')[0]
headScript.parentNode.insertBefore(script, headScript)
script.addEventListener('load', () => {
this.onLoad()
})
} else {
this.onLoad()
}
}
render() {
return <div style={{ height: `50vh` }} id={this.props.id} />
}
}
Use it like so:
// src/pages/contact.js
import React from 'react'
import Map from '../components/Map'
const center = { lat: 50, lng: 10 }
const mapProps = {
options: {
center,
zoom: 8,
},
onMount: map => {
new window.google.maps.Marker({
position: center,
map,
title: 'Europe',
})
},
}
export default function Contact() {
return (
<>
<h1>Contact</h1>
<Map id="contactMap" {...mapProps} />
</>
)
}
What woked for me was to create a gatsby-ssr.js file in the root of my project, and then include the script there, like this:
import React from "react"
export function onRenderBody({ setHeadComponents }) {
setHeadComponents([
<script
key="abc"
type="text/javascript"
src={`https://maps.googleapis.com/maps/api/js?key=${process.env.GATSBY_API_KEY}&libraries=places`}
/>,
])
}
Don't forget to include GATSBY_API_KEY or whatever you want to call it in your .env.development and .env.production files:
GATSBY_API_KEY=...

Resources