Redux/React - 为什么我不能将状态绑定到 iframe 中的组件上的 this.props?

Redux/React - Why I can't bind state to this.props on component which is in iframe?

我的react/redux/express通用项目(服务器端渲染)中有一个特定的场景。

(1)首先我这样定义我的路线:[routes.jsx]

export default (
  <Route component={App} path="/">
    <Route component={MainView} path="main">
      <IndexRoute component={ThemeIndex}></IndexRoute>
    </Route>
    <Route component={AnotherView} path="preview" />
  </Route>
);

如你所见,当url路由为:localhost:3000/preview时,react-router将使用AnotherView组件。

(2)现在关注 ThemeIndex 组件:[ ThemeIndex.jsx ]

export default class ThemeIndex extends Component {
  render() {
    return (
      <div>
        <h2>Index</h2>
        <Frame />
        <Control />
      </div>
    ); 
  }
}

(3)框架组件如下:[Frame.jsx]

class Frame extends Component {
  render() {
    const { text, uid } = this.props.infos;
    const themeUrl = `http://localhost:3000/preview?id=${uid}`;
    //console.log('Frame theme:', text);
    //console.log('Frame uid:', uid);
    return (
      <div className="col-md-8 panel panel-default">
        <div className="embed-responsive embed-responsive-16by9 panel-body">
          <iframe src={themeUrl}></iframe>
        </div>
        <div className="details" >
          {text}
        </div>
      </div>
    );
  }
}

export default connect(
  (state) => {
    return {
      infos: state.infos
    }
  }
)(Frame);

这里我用的是iframe标签,它的src是http://localhost:3000/preview?id=xxxx,也就是说会linkAnotherView 组件成为 iframe 的页面。

(4)像这样的另一个视图组件:

class AnotherView extends Component {
  render() {
    const { text, uid } = this.props.infos;
    //console.log('AnotherView theme:', text);
    //console.log('AnotherView uid:', uid);
    return (
      <div>
        <div >Another View</div>
        <div>
          {text}
        </div>
        <div>{uid}</div>
      </div>
    );
  }
}

export default connect(
  (state) => {
    console.log('another view trigger state:', state);
    return {
      infos: state.infos
    }
  }
)(AnotherView);

(4)而且我有用于生成动态值的控件组件:[ Component.jsx ]

class Control extends Component {
  render(){
    var uid = () => Math.random().toString(34).slice(2);

    return (
        <input
          onChange={(event) => this.props.addTodo({text:event.target.value, uid:uid()})
          />
    )
  }
}

export default connect(
  (state) => {
    return {
      infos: state.infos
    }
  }
)(Control);

(5)列出额外的文件,Action和Reducer: [ action.js ]

export function addTodo (attrs) {
  return {
    type: 'ADD_TODO',
    attrs
  };
}

[reducer.js]

export default (state = {text:'', uid:''}, action) => {
  switch(action.type) {
    case 'ADD_TODO':
      return Object.assign({}, state, action.attrs);
    default:
      return state;
  }
}

这是 server.js 上的商店配置:

app.use( (req, res) => {
  console.log('server - reducers:', reducers);

  const location = createLocation(req.url);
  const reducer  = combineReducers({infos: infosReducer});
  const store    = applyMiddleware(promiseMiddleware)(createStore)(reducer);

  match({ routes, location }, (err, redirectLocation, renderProps) => {
    
    .......

    function renderView() {
      const createElement = (Component, props) => (
        <Component
          {...props}
          radiumConfig={{ userAgent: req.headers['user-agent'] }}
        />
      );

      const InitialView = (
          <Provider store={store}>
            <RoutingContext
              {...renderProps}
              createElement={createElement} />
          </Provider>
      );

      const componentHTML = renderToString(InitialView);

      const initialState = store.getState();

      ......

我的申请状态是这样的:

{
  infos:{
    text: '',
    uid: ''
  }
}

(6)现在我在控制组件中输入一些单词。当输入 onChange 时会触发 addTodo action 函数去调度 reducer 中的 action,最终改变 state。一般来说,state的改变会影响Frame组件和AnotherView组件,因为我用了react-reduxconnect,绑定state属性到this.props在组件上。

但实际上,AnotherView组件有问题。在 Frame 组件中, console.log 值正确显示您输入的文本。在AnotherView组件中,连connect回调都会被触发(console.log will print 'another view trigger state: ...'),render中的console.logundefined,比如:

console.log('AnotherView theme:', text); //return AnotherView theme: undefined
console.log('AnotherView uid:', uid); //return AnotherView uid: undefined

我找到了主要原因:AnotherView组件在iframe中。因为如果我去掉 iframe,就直接把 AnotherView 组件放在这里,像这样:

return (
          <div className="col-md-8 panel panel-default">
            <div className="embed-responsive embed-responsive-16by9 panel-body">
              <AnotherView/>
            </div>
            <div className="details" >
              {text}
            </div>
          </div>
);

然后我可以在 AnotherView 组件中成功绑定 this.props 上的 state 属性,然后在 JSX html 上插入 {text} ,当你在Control组件上输入值时,你可以看到实时变化的值。如果我使用 iframe 将 link AnotherView 组件作为其页面,您看不到任何变化的 {text} 值,因为我的文本默认值为空字符串值。

如何在状态更改时将 state 属性绑定到处于 iframe 的组件中的 this.props

更新 我无法在 iframe 中获取最新的 state(源是 React 组件),当我在另一个组件中更改 state 时,实际上 mapStateToProps 被触发了!(意味着 iframe 源组件)但它的状态不是 mapStateToProps 函数中的最新状态。它与 react-redux 库没有关系?

这是组件中应该有的最新状态:

以下为iframe源组件,无法获取最新状态:

如果您从脚本标记在 iframe 中加载应用程序,它将加载该应用程序的单独实例。这就是 iframe 的要点:它们隔离代码。

应用程序的两个独立实例不会“看到”彼此的更新。就像您在两个单独的浏览器选项卡中打开应用程序一样。除非你在它们之间添加一些通信方法,否则它们不会共享状态。

不清楚为什么要渲染框架而不是直接渲染组件。但是,如果您确实需要框架,一个简单的选择是使用将组件 渲染到框架中 而不是在那里加载单独的应用程序。您可以为此使用像 react-frame-component 这样的库。或者,更简单地说,您可以根本不使用框架,因为不清楚它们的用途。通常人们希望他们隔离一个应用程序实例,但这似乎与您想要的相反。