Rails 5 Redux React 服务器端呈现给客户端 JavaScript 警告 'Replacing React-rendered children with a new...'

Rails 5 Redux React Server Side Rendering gives client side JavaScript warning 'Replacing React-rendered children with a new...'

更新:孤立的问题 - React-rails 和 npm 版本不同 - 在下面的自己的答案中修复

在 rails 5 上使用 rails-react 并收到此警告:

Replacing React-rendered children with a new root component. If you
intended to update the children of this node, you should instead have
the existing children update their state and render the new
components instead of calling ReactDOM.render.

网站似乎运行良好,但显然有问题:)

index.html.erb:

<div class="item">
  <%= react_component('WeatherRoot', {}, { :prerender => true } ) %>
</div>

components.js:

window.React = require('react');
window.ReactDOM = require('react-dom');
window.WeatherRoot  = require('./components/containers/WeatherRoot.es6').default;

WeatherRoot.es6.js

import React, { Component } from 'react';
import { render } from 'react-dom'
import { Provider } from 'react-redux';
import WeatherContainer from './WeatherContainer.es6';
import configureStore from '../store/configureStore.es6';

import {fetchWeather} from '../actions/weather.es6';

const store = configureStore();

// Request Data as Early as Possible
store.dispatch(fetchWeather());

export default class WeatherRoot extends Component {
  render() {
    return (
      <Provider store={store}>
        <WeatherContainer />
      </Provider>
    );
  }
}

WeatherContainer.es6

import { connect } from 'react-redux'
import Weather from '../components/Weather.es6';


export function WeatherContainerImpl(props){
    return (<div>HELLO</div>);
}

const mapStateToProps = (state, ownProps) => {
  return {
    weather: state.weather.data
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    dispatch
  }
}

const WeatherContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(WeatherContainerImpl);

export default WeatherContainer;

页面呈现:

<div data-react-class="WeatherRoot" data-react-props="{}">
  <div data-reactroot="" data-reactid="1" data-react-checksum="-951512882">HELLO</div>
</div>

由客户端呈现替换:

<div data-react-class="WeatherRoot" data-react-props="{}">
  <div data-reactid=".0">HELLO</div>
</div>

调试 ReactMount.js 显示此方法返回 true 是警告的来源:

/**
 * True if the supplied DOM node has a direct React-rendered child that is
 * not a React root element. Useful for warning in `render`,
 * `unmountComponentAtNode`, etc.
 *
 * @param {?DOMElement} node The candidate DOM node.
 * @return {boolean} True if the DOM element contains a direct child that was
 * rendered by React but is not a root element.
 * @internal
 */
function hasNonRootReactChild(node) {
  var reactRootID = getReactRootID(node);
  return reactRootID ? reactRootID !== ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false;
}

基本上,节点在服务器端渲染中的 ID 为“1”,

ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID)

Returns 此实现为空:

(id) {
  if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
    var index = id.indexOf(SEPARATOR, 1);
    return index > -1 ? id.substr(0, index) : id;
  }
  return null;
}

ID 似乎需要以 .要通过吗?

为什么服务器端呈现的 ID 不以 . 开头?

也许我有不同版本的反应?但两者看起来都像 14.8

添加一些调试:

export function WeatherContainer(props){
  console.log("WeatherContainer", React.version)
  return (<div>HELLO</div>);
}

显示这些日志:

WeatherContainer 15.2.1
WeatherContainer 0.14.8

!!!!

现在如何弄清楚我是如何获得两个版本的?

尝试将您的 WeatherRoot 响应包装在 <div> 中,如下所示:

export default class WeatherRoot extends Component {
  render() {
    return (
      <Provider store={store}>
        <div><WeatherContainer /></div>
      </Provider>
    );
  }
}

好的,我找到问题了

通过添加控制台日志,我能够看到 React 的服务器版本与客户端版本不同

export function WeatherContainer(props){
  console.log("WeatherContainer", React.version)
  return (<div>HELLO</div>);  
}

给予:

WeatherContainer 15.2.1
WeatherContainer 0.14.8

注意:由于 application.rb

中的 replay_console 设置,控制台日志在客户端可见
config.react.server_renderer_options = {
    files: ["react-server.js", "components.js"], # files to load for prerendering
    replay_console: true,                 # if true, console.* will be replayed client-side
    }

react-rails-1.8.1 捆绑包 react 15.2.1

所以在 package.json 中,我用

强制反应和反应-dom 到版本 0.14.3
npm install -S react@0.14.3
npm install -S react-dom@0.14.3

并且在 Gemfile 中,我用

将 react-rails 强制为 1.5.0
gem 'react-rails', '1.5'

现在都是 React 0.14.3

警告消失了