Fetch a variable from node_modules - node.js

I am trying to fetch a variable from node_modules to my application. I have exported it with module.exports, but when I use require('path/of/required/file/in/node_modules') it is showing 'unexpected token import'. I am trying to get the user info from node_modules since there is no official strategy for keystone to fetch the user data.
My local file
var keystone = require('keystone');
Teaching = keystone.list('teaching');
UserInfo = require('../../node_modules/keystone/admin/client/App/components/Footer/index.js').footer;
User = keystone.list(keystone.get('user model'));
console.log(User);
exports = module.exports =function(req,res){
var view = new keystone.View(req,res);
var locals = res.locals;
locals.section = 'teaching';
locals.data = {
teachingData : []
};
view.on('init',function(next){
var teaching = Teaching.model.find();
teaching.exec(function(err,results){
locals.data.teachingData = results;
//console.log(locals.data.teachingData);
next(err);
});
});
view.render('teaching');
}
Node_module file
import React from 'react';
import { css, StyleSheet } from 'aphrodite/no-important';
import { Container } from '../../elemental';
import theme from '../../../theme';
var Footer = React.createClass({
displayName: 'Footer',
propTypes: {
appversion: React.PropTypes.string,
backUrl: React.PropTypes.string,
brand: React.PropTypes.string,
user: React.PropTypes.object,
User: React.PropTypes.object, // eslint-disable-line react/sort-prop-types
version: React.PropTypes.string,
},
// Render the user
renderUser () {
const { User, user } = this.props;
if (!user) return null;
return (
<span>
<span> Signed in as </span>
<a href={`${Keystone.adminPath}/${User.path}/${user.id}`} tabIndex="-1" className={css(classes.link)}>
{user.name}
</a>
<span>.</span>
</span>
);
},
render () {
const { backUrl, brand, appversion, version } = this.props;
return (
<footer className={css(classes.footer)} data-keystone-footer>
<Container>
<a
href={backUrl}
tabIndex="-1"
className={css(classes.link)}
>
{brand + (appversion ? (' ' + appversion) : '')}
</a>
<span> powered by </span>
<a
href=""
target="_blank"
className={css(classes.link)}
tabIndex="-1"
>
VK
</a>
<span> version {version}.</span>
{this.renderUser()}
</Container>
</footer>
);
},
});
/* eslint quote-props: ["error", "as-needed"] */
const linkHoverAndFocus = {
color: theme.color.gray60,
outline: 'none',
};
const classes = StyleSheet.create({
footer: {
boxShadow: '0 -1px 0 rgba(0, 0, 0, 0.1)',
color: theme.color.gray40,
fontSize: theme.font.size.small,
paddingBottom: 30,
paddingTop: 40,
textAlign: 'center',
},
link: {
color: theme.color.gray60,
':hover': linkHoverAndFocus,
':focus': linkHoverAndFocus,
},
});
exports.footer = Footer.renderUser();

1) The "Unexpected token import" error is probably occuring because the current JavaScript environment does not support the import statement; the code you are trying to import from the Keystone node module is transpiled before it gets used normally. When Keystone uses that component, it's not an issue. But if you're importing a React component into a non-React file, you're bound to have problems. Which leads to my next question:
2) UserInfo isn't used in your code at all. You can't just use Keystone's React component for showing user information, unless you are working in a React environment (which it does not look like you are.) What is your use case for using this component?

Related

How to use Material Ui on React. getting error Invalid Hook call

`×
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app`
I am new to React, my code was working fine but I decided to use MaterialUi today, now it's giving me errors, I have tried googling but I didn't succeed. Another thing is that my this.state.events is returning an Empty array, instead of a list of events. How do I fix this?
My React-dom version is ─ react-dom#16.13.1 and React version is ─ react#16.13.1
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import { makeStyles } from "#material-ui/core/styles";
import GridList from "#material-ui/core/GridList";
import GridListTile from "#material-ui/core/GridListTile";
import GridListTileBar from "#material-ui/core/GridListTileBar";
import ListSubheader from "#material-ui/core/ListSubheader";
import IconButton from "#material-ui/core/IconButton";
export default class EventsList extends Component {
constructor(props) {
super(props);
this.state = { events: [] };
}
componentDidMount() {
axios
.get("http://localhost:9000/events/")
.then((response) => {
this.setState({ events: response.data });
})
.catch(function (error) {
console.log(error);
});
}
render() {
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
overflow: "hidden",
backgroundColor: theme.palette.background.paper,
},
gridList: {
width: 500,
height: 450,
},
icon: {
color: "rgba(255, 255, 255, 0.54)",
},
}));
const classes = useStyles();
return (
<div className={classes.root}>
<GridList cellHeight={180} className={classes.gridList}>
<GridListTile key="Subheader" cols={2} style={{ height: "auto" }}>
<ListSubheader component="div">December</ListSubheader>
</GridListTile>
{this.state.events.map((tile) => (
<GridListTile key={tile.img}>
<img src={tile.img} alt={tile.title} />
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.author}</span>}
actionIcon={
<IconButton
aria-label={`info about ${tile.title}`}
className={classes.icon}
></IconButton>
}
/>
</GridListTile>
))}
</GridList>
</div>
);
}
}
makeStyles returns a hook i.e. useStyles. You can only use hooks in functional components.
One option is to convert your class component to functional. Also make sure to put makeStyles code outside of component(you don't want to execute it on every re-render)
import React, { useEffect, useState } from "react";
// other imports
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
overflow: "hidden",
backgroundColor: theme.palette.background.paper,
},
gridList: {
width: 500,
height: 450,
},
icon: {
color: "rgba(255, 255, 255, 0.54)",
},
}));
const EventsList = () => {
const [events, setEvents] = useState([]);
const classes = useStyles();
useEffect(() => {
axios
.get("http://localhost:9000/events/")
.then((response) => {
this.setState({ events: response.data });
})
.catch(function (error) {
console.log(error);
});
}, []);
return (
<div className={classes.root}>
// rest of code...
</div>
);
};
Another option is to keep class based component intact and use withStyles.
See withStyles API doc:
Use the function signature if you need to have access to the theme. It's provided as the first argument.
import { withStyles } from "#material-ui/core";
const styles = (theme) => ({
root: {
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
overflow: "hidden",
backgroundColor: theme.palette.background.paper,
},
gridList: {
width: 500,
height: 450,
},
icon: {
color: "rgba(255, 255, 255, 0.54)",
},
});
class EventsList extends Component {
constructor(props) {
super(props);
this.state = { events: [] };
}
componentDidMount() {
axios
.get("http://localhost:9000/events/")
.then((response) => {
this.setState({ events: response.data });
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<div className={classes.root}>
<GridList cellHeight={180} className={classes.gridList}>
<GridListTile key="Subheader" cols={2} style={{ height: "auto" }}>
<ListSubheader component="div">December</ListSubheader>
</GridListTile>
{this.state.events.map((tile) => (
<GridListTile key={tile.img}>
<img src={tile.img} alt={tile.title} />
<GridListTileBar
title={tile.title}
subtitle={<span>by: {tile.author}</span>}
actionIcon={
<IconButton
aria-label={`info about ${tile.title}`}
className={classes.icon}
></IconButton>
}
/>
</GridListTile>
))}
</GridList>
</div>
);
}
}
export default withStyles(styles)(EventsList);
Well, it is pretty much what the error says. Hooks can be used only with functional components. EventsList is a class component and you are trying to use makeStyles in it.
I faced the same issue, but I noticed that terminal was showing a "compiled successfully" message, and still the browser gave me this error.
I actually installed the material-ui/icons as: sudo npm install -g #material-ui/icons
( I thought I'll install it globally as an administrator so I will not have to install material-ui, every time on different projects/react apps)
Inside the react app I was working, I just ran ( npm install #material-ui/icons ) which updated the node_modules folder inside my react app, and the error was gone and everything was working fine on the browser as well.
Solution: just run "npm install #material-ui/core" and "npm install #material-ui/icons" on the terminal, being in the react app directory your'e working in. Hopefully, everything will get fixed.

Importing Express while trying to connect front-end(React) and back-end(Node.js)

I'm really new to js and I'm trying to build a full-stack web app. I've built most of the front-end React code and my back-end file has the connection and query to the db.
I'm trying to connect both of those files together, but as soon as i try to import 'express' in any of the files, it breaks. Without importing express it works alright.
Giving me this error on the browser when it runs: "TypeError: Unable to get property 'prototype' of undefined or null reference"
Index.js (trying to import Express here)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
// import './index.css';
// var express = require('express')
// var app = express()
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
// const axios = require('axios')
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
App.js (front-end)
import React from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Box from '#material-ui/core/Box';
import { createMuiTheme } from '#material-ui/core/styles';
import { ThemeProvider } from '#material-ui/styles';
import { Typography } from '#material-ui/core';
// var express = require('express')
// var app = express()
//change the primary colours of the UI
const theme = createMuiTheme({
palette: {
primary: {
main: '#00a843',
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
},
},
});
export default class App extends React.Component
{
constructor(props) {
super(props);
this.handleEnterClick = this.handleEnterClick.bind(this);
this.handleSaveClick = this.handleSaveClick.bind(this);
this.state = {showAll: false, inputEmail: "", successfullySaved: true, newList: ""};
}
async handleEnterClick() {
//send the entered email to the server
// var xhr = new XMLHttpRequest()
// get a callback when the server responds
// xhr.addEventListener('load', () => {
// update the state of the component with the result here
// console.log(xhr.responseText)
// })
// open the request with the verb and the url
// xhr.open('PUT', '/api/controllers/AppController/enter')
// send the request
// xhr.send(JSON.stringify({ yourEmail: this.state.inputEmail }))
/*
const response = await fetch('/api/controllers/AppController/enter', {
method: 'PUT',
body: JSON.stringify({ email: this.state.inputEmail }),
})
console.log(await response.json())
*/
//update the UI to reflect the button being pressed
this.setState({showAll: true});
}
handleSaveClick() {
//save to the datbase
var xhr = new XMLHttpRequest()
// get a callback when the server responds
xhr.addEventListener('load', () => {
// update the state of the component with the result here
console.log(xhr.responseText)
})
// open the request with the verb and the url
xhr.open('PUT', '/api/controllers/AppController/save')
// send the request
xhr.send(JSON.stringify({ newList: this.state.newList }))
//update the UI to reflect the save
this.setState({showAll: false, inputEmail: "", successfullySaved: true, newList: ""});
//add a successful save message
}
render () {
return (
<form display="flex" noValidate autoComplete="off">
<ThemeProvider theme={theme}>
<div align = 'center'>
<TextField
id="tfInputEmail"
value={this.state.inputEmail}
onKeyPress={(ev) => {
if (ev.key === 'Enter') {
this.setState({showAll: true});
ev.preventDefault();
}
}}
onChange={e => this.setState({ inputEmail: e.target.value })}
style={{ width: 600}}
placeholder="first_last"
autoComplete = "email"
InputLabelProps={{
shrink: true,
style: {fontSize: 17, fontFamily: "Helvetica"}
}}
inputProps={{
style: {fontFamily: "Helvetica"}
}}
variant="outlined"
/>
<br/>
<Button id="btnEnter" variant="contained" color="primary"
style={{ marginTop: 15, marginBottom: 35, width: 600, height: 35}} onClick={this.handleEnterClick}>
Enter
</Button>
<PressedEnter show={this.state.showAll} click={this.handleSaveClick} list={this.state.newList}/>
</div>
</ThemeProvider>
</form>
);
}
}
function PressedEnter(props)
{
if(!props.show) {
return null;
}
return (
<div >
<Box fontSize={18} fontFamily="Helvetica" marginLeft="9px"
marginBottom="20px" marginTop="30px">
Check spelling and formatting before clicking 'Update your list' below. There's no error checking at the moment.
</Box>
<div width="100%" horizontal layout >
{
// REPLACE THE ABOVE DIV WITH A GRID LAYOUT FROM MATERIAL UI
}
{// Vertical layout for the textbox and its label
}
<div>
<Box fontSize={16} fontFamily="Helvetica"
marginBottom="10px" marginTop="20px">
Add/change/delete emails and separate by semicolon (;) here:
</Box>
<TextField
id="tfChangeList"
multiline
style={{ margin: 8, width: 446}}
InputLabelProps={{
shrink: true,
style: {fontSize: 20, fontFamily: "Helvetica"}
}}
inputProps={{
style: {height:300, fontFamily: "Helvetica"}
}}
variant="outlined"
/>
</div>
{// Vertical layout for the textbox and its label
}
<div>
<Box fontSize={16} fontFamily="Helvetica"
marginBottom="10px" marginTop="20px">
View your current Pulse Check list here:
</Box>
<TextField
disabled
id="tfCurrentList"
multiline
style={{ margin: 8, width: 446}}
InputLabelProps={{
shrink: true,
style: {fontSize: 20, fontFamily: "Helvetica"}
}}
inputProps={{
style: {height:300, fontFamily: "Helvetica"}
}}
variant="outlined"
/>
</div>
</div>
<br/>
<Button id="btnSave" variant="contained" color="primary" style={{ marginLeft: 8, marginBottom: 8,
marginTop: -8}} onClick={props.click}>
Save your Updates!
</Button>
</div>
);
}
);
}
Your full-stack web app should run both the client and server individually - i.e. run your Express application on localhost:3000 (the back end) and send HTTP requests to it from your client (React) running in a separate webpack server (the front end).
Because an application is "full-stack", this doesn't mean that the front and back ends are combined. These are separate. Hope this helps.

How can I modify this code so that render waits for Promise Items?

I am new to React and I load all the data from my database initially on page load but there is info I need to find in an array and apparently it isn't instant. What do I need to do to make sure the render method only renders the objects when the object promises have resolved?
I haven't tried much... I'm really stuck here.
This seems different than the other problems I've read here because I load a bunch on info in the beginning just fine but I need to call some team information every time a function is called so it isn't as simple as loading it once because the object i need is always different.
This code is the main issue. I also included the full file below:
I did some modification to the code in a edit: I realized that I just need to call the opponent team because I have the player team already.
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
Full file:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Async from 'react-promise'
import { withFirebase } from '../Firebase';
// import * as ROUTES from '../../constants/routes';
import { Container, Image, Spinner, Col, Row, Card, Accordion, Button } from 'react-bootstrap'
class PlayerGameList extends Component {
constructor(props) {
super(props);
this.state = {
loadingTeams: false,
loadingSchedule: false,
teams: [],
schedule: []
};
}
componentDidMount() {
this.setState({
loadingTeams: true,
loadingSchedule: true,
});
this.unsubscribe = this.props.firebase
.teams()
.where('players', 'array-contains', '-LXkkB7GNvYrU4UkUMle')
.onSnapshot(snapshot => {
let teams = [];
snapshot.forEach(doc =>
teams.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
teams,
loadingTeams: false,
});
});
this.unsubscribe2 = this.props.firebase
.schedule()
.onSnapshot(snap => {
let schedule = [];
snap.forEach(doc =>
schedule.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
schedule,
loadingSchedule: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsubscribe2();
}
render() {
const { teams, schedule, loadingTeams, loadingSchedule } = this.state;
return (
<div>
<h2>Games</h2>
{loadingTeams && loadingSchedule && <div colSpan="12"><Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner></div>}
{/* CONTENT */}
<Container fluid>
<Row>
{getTeams({ teams, schedule })}
</Row>
</Container>
</div >
);
}
}
function getTeams({ teams, schedule }) {
if (!teams) {
return null;
}
if (!teams.length) {
return null;
} else {
return teams.map(team => getGames({ team, schedule }))
}
}
function getGames({ team, schedule }) {
schedule.sort((a, b) => (a.time > b.time) ? -1 : 1)
if (!schedule) {
return null;
}
if (!schedule.length) {
return null;
} else {
return schedule.map(game => guts({ team, game }));
}
}
function guts({ team, game }) {
const image = {
height: '25px',
width: '25px'
}
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
if (game.team_1 === team.id || game.team_2 === team.id) {
var time = new Date(game.time.seconds * 1000);
var dateFormat = require('dateformat');
var finalTime = dateFormat(time, 'ddd mmm dd, h:MM tt')
return (
<Col lg='4' md='6' sm='12' key={game.uid} style={{ marginBottom: '15px' }}>
<Card>
<Card.Body>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'red' }}></div>
</Row>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'blue' }}></div>
</Row>
<Row>
<div>
{finalTime}
</div>
</Row>
</Card.Body>
<Accordion>
<Card style={{ margin: '0', padding: '0' }}>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Show Match IDs
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>{game.match_id}</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</Card>
</Col>
);
}
}
export default withFirebase(PlayerGameList);
The items all load blank then a few seconds later all the console logs come through with the array objects. When I tell it to await the program just throws an error.

React Nodejs - Custom #font-face in jsx for PDF generation

I have a React project in which part of the functionality involves sending a request to a Nodejs server that runs PhantomJS and the html-pdf plugin in order to create a pdf that gets build via a react component from my node server. The problem I'm having is actually embedding custom fonts into my react component. I have a base64 encoded font set that would be easier to include versus a bunch of files, but due to how React loads in CSS, I keep getting errors.
Here is the function that gets called to generate the PDF on my NodeJS server:
exports.order = (req, res, next) => {
phantom.create().then(function(ph) {
ph.createPage().then(function(page) {
// page.viewportSize = { width: 600, height: 600 };
page.open(`http://localhost:3005/print/work/${req.params.id}`).then(function(status) {
page.property('content').then(function(content) {
pdf.create(content, options).toBuffer(function(err, buffer){
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename=order-${req.params.id}.pdf`,
'Content-Length': buffer.length
});
res.end(buffer);
});
page.close();
ph.exit();
});
});
});
});
}
Here's the actual React component that contains the HTML for the PDF:
var React = require('react');
var moment = require('moment');
class PrintWorkOrder extends React.Component {
render() {
var order = this.props;
var s = {
body: {
width: '100%',
float: 'none',
fontFamily: 'Helvetica',
color: '#000',
display: 'block',
fontSize: 10,
}
}
return (
<div style={s.body}>
I shortened the content in her for brevity
</div>
)
}
}
module.exports = PrintWorkOrder;
The functionality currently works, it's just now I need to add some custom fonts that aren't Helvetica and I'm having trouble solving that with this current implementation. Does anyone have any insight into this?
I also have the same issue, what I did is to encoded front into base64 and put it as
font.css
#font-face {
font-family: 'fontName';
src: url(data:application/x-font-woff;charset=utf-8;base64,<base64>) format('woff'),
font-weight: normal;
font-style: normal;
}
The trick is to read CSS file content and inject it into the DOM markup as style tag
React.createElement('style', { dangerouslySetInnerHTML: { __html: this.props.children } });
So to do it:
For the second line of code, what it does it to create style element. You could do is to write a component that read content from the .css file(s) and then put the content inside style element like this:
Style.js
var React = require('react');
export default class Style extends React.Component {
static propTypes = {
children: React.PropTypes.string.isRequired
};
render() {
return React.createElement('style', { dangerouslySetInnerHTML: { __html: this.props.children } });
}
}
Modify PrintWorkOrder.js
var React = require('react');
var moment = require('moment');
var fs = require('fs');
var path = require('path');
var Style = require('./Style');
const readFile = (filePath) => fs.readFileSync(path.resolve(__dirname, filePath)).toString();
class PrintWorkOrder extends React.Component {
constructor(props) {
super(props);
this.state = {
styles: []
}
}
componentWillMount() {
this.setState({
styles: [
readFile('font.css') // Assuming putting font.css same location with PrintWorkOrder component
]
})
}
render() {
var order = this.props;
var s = {
body: {
width: '100%',
float: 'none',
fontFamily: 'Helvetica',
color: '#000',
display: 'block',
fontSize: 10,
}
}
return (
<html>
<head>
{/* Here to render all the styles */}
{this.state.styles.map(s => (<Style>{s}</Style>))}
</head>
<body>
<div style={s.body}>
I shortened the content in her for brevity
</div>
</body>
</html>
);
}
}
module.exports = PrintWorkOrder;

How can I use Esri Arcgis Map in ReactJs Project?

I'm trying to use Esri map. To include map in my project, here is what I found:
require([
"esri/map",
"esri/dijit/Search",
"esri/dijit/LocateButton",
"esri/geometry/Point",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
But there isn't any esri folder or npm package. Therefore, I'm confused here. How esri is imported in project?
Use esri-loader to load the required esri modules. This is a component rendering basemap.
import React, { Component } from 'react';
import { loadModules } from 'esri-loader';
const options = {
url: 'https://js.arcgis.com/4.6/'
};
const styles = {
container: {
height: '100vh',
width: '100vw'
},
mapDiv: {
padding: 0,
margin: 0,
height: '100%',
width: '100%'
},
}
class BaseMap extends Component {
constructor(props) {
super(props);
this.state = {
status: 'loading'
}
}
componentDidMount() {
loadModules(['esri/Map', 'esri/views/MapView'], options)
.then(([Map, MapView]) => {
const map = new Map({ basemap: "streets" });
const view = new MapView({
container: "viewDiv",
map,
zoom: 15,
center: [78.4867, 17.3850]
});
view.then(() => {
this.setState({
map,
view,
status: 'loaded'
});
});
})
}
renderMap() {
if(this.state.status === 'loading') {
return <div>loading</div>;
}
}
render() {
return(
<div style={styles.container}>
<div id='viewDiv' style={ styles.mapDiv } >
{this.renderMap()}
</div>
</div>
)
}
}
export default BaseMap;
This renders a base map but this is not responsive. If I remove the div around the view div or if I give the height and width of the outer div (surrounding viewDiv) as relative ({ height: '100%', width: '100%'}), the map does not render. No idea why. Any suggestions to make it responsive would be appreciated.
An alternative method to the above is the one demonstrated in esri-react-router-example. That application uses a library called esri-loader to lazy load the ArcGIS API only in components/routes where it is needed. Example:
First, install the esri-loader libary:
npm install esri-loader --save
Then import the esri-loader functions in any react module:
import * as esriLoader from 'esri-loader'
Then lazy load the ArcGIS API:
componentDidMount () {
if (!esriLoader.isLoaded()) {
// lazy load the arcgis api
const options = {
// use a specific version instead of latest 4.x
url: '//js.arcgis.com/3.18compact/'
}
esriLoader.bootstrap((err) => {
if (err) {
console.error(err)
}
// now that the arcgis api has loaded, we can create the map
this._createMap()
}, options)
} else {
// arcgis api is already loaded, just create the map
this._createMap()
}
},
Then load and the ArcGIS API's (Dojo) modules that are needed to create a map:
_createMap () {
// get item id from route params or use default
const itemId = this.props.params.itemId || '8e42e164d4174da09f61fe0d3f206641'
// require the map class
esriLoader.dojoRequire(['esri/arcgis/utils'], (arcgisUtils) => {
// create a map at a DOM node in this component
arcgisUtils.createMap(itemId, this.refs.map)
.then((response) => {
// hide the loading indicator
// and show the map title
// NOTE: this will trigger a rerender
this.setState({
mapLoaded: true,
item: response.itemInfo.item
})
})
})
}
The benefit of using esri-loader over the approach shown above is that you don't have to use the Dojo loader and toolchain to load and build your entire application. You can use the React toolchain of your choice (webpack, etc).
This blog post explains how this approach works and compares it to other (similar) approaches used in applications like esri-redux.
You don't need to import esri api like you do for ReactJS. As the react file will finally compile into a js file you need to write the esri parts as it is and mix the ReactJS part for handling the dom node, which is the main purpose of ReactJS.
A sample from the links below is here
define([
'react',
'esri/toolbars/draw',
'esri/geometry/geometryEngine',
'dojo/topic',
'dojo/on',
'helpers/NumFormatter'
], function(
React,
Draw, geomEngine,
topic, on,
format
) {
var fixed = format(3);
var DrawToolWidget = React.createClass({
getInitialState: function() {
return {
startPoint: null,
btnText: 'Draw Line',
distance: 0,
x: 0,
y: 0
};
},
componentDidMount: function() {
this.draw = new Draw(this.props.map);
this.handler = this.draw.on('draw-end', this.onDrawEnd);
this.subscriber = topic.subscribe(
'map-mouse-move', this.mapCoordsUpdate
);
},
componentWillUnMount: function() {
this.handler.remove();
this.subscriber.remove();
},
onDrawEnd: function(e) {
this.draw.deactivate();
this.setState({
startPoint: null,
btnText: 'Draw Line'
});
},
mapCoordsUpdate: function(data) {
this.setState(data);
// not sure I like this conditional check
if (this.state.startPoint) {
this.updateDistance(data);
}
},
updateDistance: function(endPoint) {
var distance = geomEngine.distance(this.state.startPoint, endPoint);
this.setState({ distance: distance });
},
drawLine: function() {
this.setState({ btnText: 'Drawing...' });
this.draw.activate(Draw.POLYLINE);
on.once(this.props.map, 'click', function(e) {
this.setState({ startPoint: e.mapPoint });
// soo hacky, but Draw.LINE interaction is odd to use
on.once(this.props.map, 'click', function() {
this.onDrawEnd();
}.bind(this));
}.bind(this))
},
render: function() {
return (
<div className='well'>
<button className='btn btn-primary' onClick={this.drawLine}>
{this.state.btnText}
</button>
<hr />
<p>
<label>Distance: {fixed(this.state.distance)}</label>
</p>
</div>
);
}
});
return DrawToolWidget;
});
Below are the links where you can find information in detail.
http://odoe.net/blog/esrijs-reactjs/
https://geonet.esri.com/people/odoe/blog/2015/04/01/esrijs-with-reactjs-updated

Resources