I'm trying to get the react-router up and running, but somehow React is not defined within react-router and because of that fails with the error
Uncaught TypeError: Cannot read property 'createClass' of undefined
I'm also getting this error later:
Uncaught Error: Load timeout for modules: jsx!testapp_unnormalized2,jsx!testapp
Any idea of what I'm doing wrong here? I simply want to get the router working without concatenating all the files.
This is how the app looks:
index.html
<!-- ... -->
<script data-main="../resources/js/init" src="../resources/bower_components/requirejs/require.js"></script>
</body>
<!-- ... -->
init.js
require.config({
paths: {
react: "/resources/bower_components/react/react-with-addons",
JSXTransformer: "/resources/bower_components/jsx-requirejs-plugin/js/JSXTransformer",
jsx: "/resources/bower_components/jsx-requirejs-plugin/js/jsx",
jquery : "/resources/bower_components/jquery/dist/jquery",
'react-router' : "/resources/bower_components/react-router/dist/react-router",
'react-router-shim': 'react-router-shim'
},
shim : {
'react-router': {
deps: ['react'],
exports: 'Router'
}
},
});
require(['jsx!testapp'], function(App){
var app = new App();
app.init();
});
react-router-shim.js
define(['react'], function(React) {
"use strict";
window.React = React;
});
testapp.js
define(function(require){
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;
var NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
var RouteHandler = Router.RouteHandler;
var routes = (
<Route handler={Home} path="/">
<DefaultRoute handler={Home} />
</Route>
);
var Home = React.createClass({
render: function() {
return(
<p>This is the mathias page</p>
);
}
});
var App;
App.init = function () {
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, document.getElementById('content'));
});
};
return App;
});
react-router doesn't support AMD and thus won't load the React dependency by default. Instead, you should load React first and set it as a global (window.React = React), or use the RequireJS shim configuration to load React as a global.
See react-router#171 for more discussion around react-router and AMD.
Related
I'm trying to use React with my Express backend. Currently, i'm using React as my template language.
My views are .jsx files.
/* Express */
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const session = require('express-session');
...
/* Express application parameters */
app
.set('views', __dirname + '/views')
.set('view engine', 'jsx')
.engine('jsx', require('express-react-views').createEngine())
.use(bodyParser.urlencoded({ extended: false }))
.use(sessionStorage)
.use(express.static('public'));
// Express routing defined in ./routes/index.js
require('./routes')(app);
index.js
/* Express routing */
module.exports = function (app) {
app.get('/subscribe', require('./subscribe').get);
app.post('/subscribe', require('./subscribe').post);
app.get('/', require('./login').get);
app.post('/', require('./login').post);
app.all('/userlist', require('./home').all);
app.get('/lobby', require('./lobby').get);
app.get('/playdario', require('./game').get);
app.post('/playdario', require('./game').post);
app.all('/logout', require('./logout').all);
}
This is my route handler for the subscription page :
subscribe.js
exports.get = (req, res) => {
res.render('subscribe', { 'title': 'Subscription' });
}
I'm trying as a test (like in React tutorial) to refresh a Clock component at each second.
subscribe.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var DefaultLayout = require('./layouts/default');
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<DefaultLayout>
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
</DefaultLayout>
);
}
}
module.exports = Clock;
All of my views are composed by a default component which contains the html tags.
default.jsx
var React = require('react');
class DefaultLayout extends React.Component {
render() {
return (
<html>
<head><title>{this.props.title}</title></head>
<body>
{this.props.children}
</body>
</html>
);
}
}
module.exports = DefaultLayout;
But the problem is that the clock is displayed only one time, when I reach the page. But clock is not refreshing at all. I tried another example with a button that is changing text value placed in his state but it's not working too...
Must I use **.jsx* files in my res.render ? When i send an html file like I did at beginning of my project, i did not found how to send informations from Express through my html file like I can do with views like .twig or .jsx...
What I did wrong?
I don't believe that express-react-views actually mounts the components. As per its documentation:
This is an Express view engine which renders React components on
server. It renders static markup and does not support mounting those
views on the client.
So no components are mounted and only the markup generated is rendered. That means that componentDidMount is never called because the component itself is never instantiated in browser. If you want the React component to interact with elements, you'll need to render it client-side.
Since it is not re-rendering, I assume tick() is not triggered, because the state is updated there. Try doing the following:
componentDidMount() {
this.timerID = setInterval(
this.tick,
1000
);
}
or
componentDidMount() {
var self = this
this.timerID = setInterval(
() => self.tick(),
1000
);
}
since this is not in the scope of the arrow function
I'm trying to use DRY in React JS. I'm trying to use the same HTML partial in different files
partial:
var AdminMenu = React.createClass({
getInitialState: function() {
return {};
},
render: function() {
return (
<h1>Menu</h1>
);
}
});
I'm requeiring it in another file:
require('./scripts/adminMenu.js');
ReactDOM.render(
<AdminMenu/>,
document.getElementById('content')
);
But I'm getting an error:
Uncaught ReferenceError: require is not defined
this scripts are included on html page like:
<script type="text/babel" src="scripts/admin.js"></script>
I'm using webpack
If you are not using any module bundler like webpack or etc.
You should assign you components to some javascript global object, because objects from .jsx are not put in global scope
So here is the solution (used window object here)
Defined module:
window.AdminMenu = React.createClass({
getInitialState: function() {
return {};
},
render: function() {
return (
<h1>Menu</h1>
);
}
});
Where you use it:
ReactDOM.render(
<window.AdminMenu/>,
document.getElementById('content')
);
You have to use
const { Component } = React;
const { render } = ReactDOM;
in place of
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
Consider to use ES6 modules instead of require with React
export a module:
// src/hello.js
// export as default
export default const hello = ({name}) => (
<h1>Hello {name}</h1>
)
import a module:
// src/index.js
// import from relative directory (src)
import Hello from './hello'
const App = () => {
return (
<div>
<Hello name="Pavel" />
</div>
)
}
You should read more about modules for example here: https://www.sitepoint.com/understanding-es6-modules/
The main problems in your existing code are:
It looks, in spite of that you are using Webpack, you use it wrong way. In your final bundle (final JS file), it shouldn't contain any 'require' keywords. Did you use Babel with your webpack? Please, show us your WebPack config.
Your AdminMenu file looks not like module. Your adminMenu file should contain 'export' keyword, after that you will be able to 'require' or 'import' it from other files.
Also, you can write questions in comments with Russian if it is more convenient for you
I'm using django-pipeline along with browserify based on the documentation here -
http://gregblogs.com/how-django-reactjs-and-browserify/
I have it working perfectly fine when loading NPM/Bower packages like so -
'build_js': {
'source_filenames': (
'js/bower_components/jquery/dist/jquery.js',
'bootstrap/js/bootstrap.js',
'js/bower_components/react/react-with-addons.js',
'js/bower_components/react/react-dom.js',
'datatables/js/jquery.dataTables.js',
'datatables/js/dataTables.bootstrap.js',
'js/node_modules/marked/marked.min.js',
'js/node_modules/react-router/umd/ReactRouter.js',
'js/child.js',
'js/parent.js',
'js/build.browserify.js',
),
'output_filename': 'js/build_js.js',
The problem is I'm trying to reference the child.js and parent.js within the build.browserify.js
This is the contents of the 3 files -
child.js
var Child = React.createClass({
render: function(){
return (
<div>
and this is the <b>{this.props.name}</b>.
</div>
)
}
});
parent.js
var Parent = React.createClass({
render: function(){
return (
<div>
<div> This is the parent. </div>
<Child name="child"/>
</div>
)
}
});
build.browserify.js
ReactDOM.render(
<Parent />,
document.getElementById('content')
);
I actually get 3 errors in my browser -
The following happens on my child.js and parent.js files both on line 4 -
Uncaught SyntaxError: Unexpected token <
And then I get this on my build.browserify.browserified.js at line 3
Uncaught ReferenceError: Parent is not defined
This is the contents of that file -
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
ReactDOM.render(
React.createElement(Parent, null),
document.getElementById('content')
);
},{}]},{},[1]);
edit -
If I put all code in a single build.browserify.js file like this it works -
var Child = React.createClass({
render: function(){
return (
<div>
and this is the <b>{this.props.name}</b>.
</div>
)
}
});
var Parent = React.createClass({
render: function(){
return (
<div>
<div> This is the parent. </div>
<Child name="child"/>
</div>
)
}
});
ReactDOM.render(
<Parent />,
document.getElementById('app')
);
#taylorc93 is on the right track with this, but you're missing an additional step.
In addition to having to do require('./parent') in any file where you want to include the parent module, you also need to actually export the content of the parent.js file. So, parent.js should look like:
child.js
var React = require('react');
modules.export = React.createClass({
displayName: 'Child', // Always setting React component's displayName will make your error messages easier to understand
render: function(){
return (
<div>
and this is the <b>{this.props.name}</b>.
</div>
)
}
});
parent.js
var React = require('react');
var Child = require('./child');
modules.export = React.createClass({
displayName: 'Parent', // Always setting React component's displayName will make your error messages easier to understand
render: function(){
return (
<div>
<div> This is the parent. </div>
<Child name="child"/>
</div>
)
}
});
build.browserify.js
var ReactDOM = require('react-dom');
var Parent = require('./parent');
ReactDOM.render(
<Parent />,
document.getElementById('app')
);
Also, while not required, it's good practice to give Component files uppercase names, just like you would class files in Java. Most apps will also name the root file as app.js or main.js or something like that, rather than build.browserify.js which is a bit vague since technically the file has nothing to do with building or with Browserify.
I'm looking into the Shopify Embedded App SDK and theres a part where you have to drop a bit of javascript from server-side variables. I'm using react engine, react-router and express.js.
<head>
<script src="https://cdn.shopify.com/s/assets/external/app.js"></script>
<script type="text/javascript">
ShopifyApp.init({
apiKey: 'YOUR_APP_API_KEY',
shopOrigin: 'https://CURRENT_LOGGED_IN_SHOP.myshopify.com'
});
</script>
</head>
There is a way to provide red.render(req.url, data) data, however I'm not sure what I'm supposed to do with it from within a component. Is there a way to access and change the global ShopifyApp variable from within a component?
I added the shopify code to the componentDidMount of the App component (top most component in react-routes).
var Layout = require('./layout.jsx');
var React = require('react');
var Router = require('react-router');
module.exports = React.createClass({
componentDidMount: function(){
if(this.props.shopify){
ShopifyApp.init({
apiKey: this.props.shopify.apiKey,
shopOrigin: this.props.shopify.shopOrigin
})
}
},
render: function render() {
return (
<Layout {...this.props}>
<Router.RouteHandler {...this.props}/>
</Layout>
);
}
});
I've followed a couple of examples in an attempt to get access to a parameter from a Route in the React component that handles it. However the result of console.log on this.props from inside the render or componentDidMount is always {} when I'd expect it to contain the gameId from the gamesView route.
client.js which starts the Router:
// HTML5 History API fix for local
if (config.environment === 'dev') {
var router = Router.create({ routes: routes });
} else {
var router = Router.create({ routes: routes, location: Router.HistoryLocation });
}
router.run(function(Handler) {
React.render(
<Handler />,
document.getElementById('app')
);
});
routes.js with some routes removed for simplicity:
var routes = (
<Route name='home' path='/' handler={app}>
<DefaultRoute handler={home} location="/" />
<Route name='gamesView' path='/games/:gameId' handler={gamesView} />
</Route>
);
module.exports = routes;
...and app.js which wraps the other routes, I've tried it both with and without {...this.props} in the RouteHandler. If I console.log(this.props) from inside the render function here is also returns {}:
var App = React.createClass({
render: function() {
return (
<div className='container'>
<div className='row'>
<RouteHandler {...this.props} />
</div>
</div>
);
}
});
module.exports = App;
Finally the gamesView React component that I expect to see the props object. Here this.props is also {} and the following results in the error TypeError: $__0 is undefined var $__0= this.props.params,gameId=$__0.gameId;:
var GamesView = React.createClass({
render: function() {
var { gameId } = this.props.params;
return (
<div>
<h1>Game Name</h1>
<p>{gameId}</p>
</div>
);
}
});
module.exports = GamesView;
Anybody have any ideas?
You won't see those params until you are at the component defined in your router. App won't know anything about them. If you put the console.log(this.props.params) in your gamesView component, however, you should see them.
After discussing on the React Router (RR) Github it turned out this was due to me using an older version of RR (v0.11.6).
Looking at the example in the docs for that release it showed that I needed to use a Router.State mixin and then get the expected param via var gameId = this.getParams().gameId;.
Without upgrading RR the working version of my original example for GamesView becomes:
var React = require('react');
var Router = require('react-router');
var { Route, RouteHandler, Link } = Router;
var GamesView = React.createClass({
mixins: [ Router.State ],
render: function() {
var gameId = this.getParams().gameId;
return (
<div>
<h1>Game Name</h1>
<p>{gameId}</p>
</div>
);
}
});
module.exports = GamesView;