Изоморфный React — как сделать функцию реакции частью объекта окна

Я создавал изоморфное реагирующее приложение, используя node-jsx, browserify, reactify и т. д. Мой код отлично работает на сервере, а компоненты монтируются и отображаются правильно. Однако функция реагирования, похоже, не работает, например, функция handleClick не распознает alert() или console.log() никогда не выводит ожидаемый результат ни на сервер, ни на клиентскую консоль. Кто-нибудь может определить, что здесь происходит?

ОБНОВЛЕНИЕ: Еще одна вещь, которую я хочу отметить, это то, что когда я запускаю сервер и перехожу в браузер, в консоли браузера (используется инструмент разработчика Chrome) я набрал «window.React», он фактически вернул объект React. Но console.log по-прежнему ничего не делает для функции обработчика кликов.

представления/index.ejs

<!doctype html>
<html>
<head>
    <title>Shortened URL Generator</title>
    <link href='/style.css' rel="stylesheet">
    <link href="css/griddle.css" rel="stylesheet" />
</head>
<body>
<h1 id="main-title">Welcome to Shortened URL Generator</h1>

<div id="react-main-mount">
    <%- reactOutput %>
</div>

<!-- comment out main.js to see server side only rendering -->
<script src="https://fb.me/react-with-addons-0.14.3.min.js"></script>
<script src="https://fb.me/react-dom-0.14.3.min.js"></script>
<script src="/main.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/JSXTransformer-0.12.0.js"></script>
<script type="text/javascript" src="scripts/griddle.js"></script>

</body>
</html>

маршруты/routes.js

    var React = require('react/addons'),
    ReactApp = React.createFactory(require('../components/ReactApp'));

    module.exports = function(app) {
    var storeUrls = {
        "fb.com": "facebook.com"
    };
    app.get('/', function(req, res){
        // React.renderToString takes your component
        // and generates the markup
        var reactHtml = React.renderToString(ReactApp({}));
        // Output html rendered by react
        // console.log(myAppHtml);
        res.render('index.ejs', {reactOutput: reactHtml});
    });
    app.get('/:routeParam', function(req, res){

    });

};

приложение/компоненты/ReactApp.js

var TableComponent = require('./TableComponent');
var React = require('react/addons');
var urls = require('./url');
var Griddle = React.createFactory(require('griddle-react'));
var ReactApp = React.createClass({

componentDidMount: function () {
    console.log("yes");

},
handleClick: function() {
   // this.setState({liked: !this.state.liked});
    var longUrl = this.refs.inputUrl;
    urls.push({
        "original url": longUrl,
        "shortened url": "/"
    })
    console.log(longurl);

},
render: function () {
    return (
        <div>
        <div id="form">
            <form>
             <section>Paste your long url here</section>
             <input ref="inputUrl"  value={this.props.value} type="text" placeholder="http://...." />
             <button onclick={this.handleClick} type="submit" value="Submit">Shorten URL</button>
            </form>
        </div>
            <div id="table-area">
                <TableComponent />
            </div>
        </div>
    )
}
});

module.exports = ReactApp;

приложение/main.js

var React = require('react/addons');
var ReactApp = require('./components/ReactApp');
var TableComponent = require('./components/TableComponent');

var mountNode = document.getElementById('react-main-mount');
var mountTable= document.getElementById('table-area');

React.render(new ReactApp({}), mountNode);
React.render(new TableComponent({}), mountTable);

server.js

var express = require('express'),
path = require('path'),
app = express(),
port = 5000,
bodyParser = require('body-parser');

require('node-jsx').install();


// Include static assets. Not advised for production
app.use(express.static(path.join(__dirname, 'public')));
// Set view path
app.set('views', path.join(__dirname, 'views'));
// set up ejs for templating. You can use whatever
app.set('view engine', 'ejs');

// Set up Routes for the application
require('./app/routes/routes.js')(app);

//Route not found -- Set 404
app.get('*', function(req, res) {
    res.json({
    'route': 'Sorry this page does not exist!'
});
});

app.listen(port);
console.log('Server is Up and Running at Port : ' + port);

Gulpfile.js

var gulp = require('gulp'); var source = require('vinyl-source-stream'), browserify = require('browserify'); gulp.task('scripts', function(){ return browserify({ transform: [ 'reactify' ], entries: 'app/main.js' }) .bundle() .pipe(source('main.js')) .pipe(gulp.dest('./public/')); }); gulp.task('default', ['scripts']);

person mikuya707    schedule 11.12.2015    source источник
comment
А как насчет вашего взгляда? Сценарий включен в <head> или в конец <body>?   -  person Louay Alakkad    schedule 11.12.2015
comment
Привет, @Louy, пожалуйста, ознакомься с моими обновленными представлениями и маршрутами.   -  person mikuya707    schedule 11.12.2015
comment
Вы проверяли консоль браузера? Вы получаете какие-либо ошибки?   -  person Louay Alakkad    schedule 12.12.2015


Ответы (3)


document.getElementById('react-main-mount'); возвращает null, если ваш скрипт запускается до загрузки этого элемента в html.

Что вам нужно сделать, так это либо включить свой сценарий перед закрывающим тегом </body>, либо запустить ReactDOM.render после DOMContentLoaded.

person Louay Alakkad    schedule 11.12.2015
comment
Привет, спасибо за предложение. мои теги script включены перед закрывающим тегом ‹/body›, но не уверен, что вы подразумеваете под запуском ReactDOM.render после DOMContentLoaded, не могли бы вы объяснить немного больше? - person mikuya707; 11.12.2015
comment
Что ж, в таком случае этот ответ, вероятно, не имеет значения. В любом случае прочтите статью, на которую я дал ссылку. Вы должны знать об этом все, когда занимаетесь интерфейсной разработкой. - person Louay Alakkad; 12.12.2015

Это может быть простая модификация вашего существующего кода:

if (typeof window !== 'undefined) {
    window.React = require('react');
}

Сделайте это для всего, что вы хотите сделать доступным глобально, обычно вы захотите сделать то же самое и для ReactDOM, чтобы вы могли вызывать рендеринг непосредственно в html-файле. Хотя обычно это не рекомендуется. Вы можете использовать отдельные загрузки из React Downloads.

person Martin Malloy    schedule 11.12.2015
comment
Спасибо за предложение! Я попробовал ваше решение, но оно выдает ReferenceError: окно не определено, когда я пытаюсь запустить сервер - person mikuya707; 11.12.2015
comment
Проверьте обновленный пример кода. Надеюсь, это решит вашу проблему. - person Martin Malloy; 11.12.2015
comment
Я тоже пробовал это, но похоже, что он не распознает объект окна. Он все еще выдает ошибку ссылки - person mikuya707; 11.12.2015

Итак, я понял, что это как-то связано с React.render в файле main.js. вместо

React.render(new ReactApp({}), mountNode);

должен быть

React.render(<ReactApp/>, document.getElementById('react-main-mount'));

В этом случае TableComponent не нужно рендерить, также требуется React без использования аддона.

Теперь это работает. Спасибо всем за помощь!

person mikuya707    schedule 11.12.2015