React 服务器端渲染 AJAX setState() - 未定义文档

Posted

技术标签:

【中文标题】React 服务器端渲染 AJAX setState() - 未定义文档【英文标题】:React server-side rendering AJAX setState() - document is not defined 【发布时间】:2016-07-16 15:23:51 【问题描述】:

如果没有来自 AJAX 请求的响应,我将无法向用户显示任何内容,即我将所有内容都存储在数据库中。 我的组件 GenericPage.jsx

export default class GenericPage extends React.Component 
componentWillMount() 
        if (this.store && this.onStoreUpdated) this.store.addChangeListener(this.onStoreUpdated);
        if (!this.state.page && this.state.pageId) 
            this.fetchData();
        
    
onPageStoreUpdated() 
        console.info('onPageStoreUpdated');
        var page = PageStore.getCurrentObject();
        this.setState(page: page, loaded: true); // the error goes away if I comment this out
    
    render() 
        ...
    

我的 Express JS 服务器代码:

server.get('*', function (req, res) 
        res.header("Access-Control-Allow-Origin", "*");
        match(routes, location: req.url, (error, redirectLocation, renderProps) => 
            if (error) 
                res.send(500, error.message)
             else if (redirectLocation) 
                res.redirect(302, redirectLocation.pathname + redirectLocation.search)
             else if (renderProps) 
                let htmlStr = React.renderToString(<RoutingContext ...renderProps />);
                res.header("Access-Control-Allow-Origin", "*");
                res.render('layout',  reactHtml: htmlStr );
             else 
                console.log('not found')
                res.send(404, 'Not found')
            
        )
    );

完整的堆栈跟踪:

/Users/eric/af/frontend_app/node_modules/react/lib/getActiveElement.js:23
    return document.body;
           ^
ReferenceError: document is not defined
    at getActiveElement (/Users/eric/af/frontend_app/node_modules/react/lib/getActiveElement.js:23:12)
    at ReactReconcileTransaction.ReactInputSelection.getSelectionInformation (/Users/eric/af/frontend_app/node_modules/react/lib/ReactInputSelection.js:40:23)
    at ReactReconcileTransaction.Mixin.initializeAll (/Users/eric/af/frontend_app/node_modules/react/lib/Transaction.js:168:30)
    at ReactReconcileTransaction.Mixin.perform (/Users/eric/af/frontend_app/node_modules/react/lib/Transaction.js:133:12)
    at ReactUpdatesFlushTransaction.Mixin.perform (/Users/eric/af/frontend_app/node_modules/react/lib/Transaction.js:134:20)
    at ReactUpdatesFlushTransaction.assign.perform (/Users/eric/af/frontend_app/node_modules/react/lib/ReactUpdates.js:95:38)
    at Object.flushBatchedUpdates (/Users/eric/af/frontend_app/node_modules/react/lib/ReactUpdates.js:175:19)
    at Object.wrapper [as flushBatchedUpdates] (/Users/eric/af/frontend_app/node_modules/react/lib/ReactPerf.js:70:21)
    at ReactDefaultBatchingStrategyTransaction.Mixin.closeAll (/Users/eric/af/frontend_app/node_modules/react/lib/Transaction.js:207:25)
    at ReactDefaultBatchingStrategyTransaction.Mixin.perform (/Users/eric/af/frontend_app/node_modules/react/lib/Transaction.js:148:16)

我正在使用react 0.13.3 和react-router 1.0.0

【问题讨论】:

这里的诀窍是在加载应用程序之前通过 Promise 收集数据,然后从这些数据中混合状态。我将无耻地在这里插入我自己的实现,因为它解释了太多的代码和概念,需要一个相关的答案:github.com/limelights/react-uniini @HenrikAndersson 有趣的是,这正是我过去半小时所做的,似乎效果很好 不错。这是正确的做法。 【参考方案1】:

如果您只在浏览器中加载数据,请尝试将您的 ComponentWillMount 代码移动到 ComponentDidMount

【讨论】:

我需要从数据库中获取页面并返回给最终用户,否则他们将什么都看不到。从技术上讲,我可以将这些初始数据作为“hello world”返回,并让客户端脚本负责获取数据。但是,用户首先会看到一个空白页面。搜索引擎可能总是看不到任何违背在此处进行 React 服务器端渲染的目的 换句话说,这不是我的选择 有道理,我误解了。您似乎遇到了这个错误:github.com/facebook/react/issues/5779【参考方案2】:

事实证明,在服务器端使用 React 组件是不可能实现的。 因此,我将 AJAX 调用移至我的 React 组件中的 componentDidMount,这样它们就不会影响服务器端。 我需要在服务器端获取的数据,在渲染 React 组件之前,我在服务器端 Node JS 代码中获取它们,在任何 React 组件之外:

server.get('*', function (req, res) 
        res.header("Access-Control-Allow-Origin", "*");
        match(routes, location: req.url, (error, redirectLocation, renderProps) => 
            if (renderProps) 
                someAsyncHttpRequest(someUrl)
                    .onSuccess((response) => 
                         page = response.body;
                         renderProps.params.page = page;

                         Dispatcher.dispatch(
                              actionType: AppAction.PAGE_FETCHED,
                              data: page
                         );
                    );
                let htmlStr = React.renderToString(<RoutingContext ...renderProps />);
                res.render('layout',  reactHtml: htmlStr );
            
        )
    );

【讨论】:

以上是关于React 服务器端渲染 AJAX setState() - 未定义文档的主要内容,如果未能解决你的问题,请参考以下文章

react + redux + saga +服务器端渲染+如何停止服务器端渲染页面的额外ajax调用?

React进行数据处理

React进行数据处理

React 数据改变后页面没有重新渲染

React中setState同步更新策略

react 生命周期