【问题标题】:Redux/React - Why I can't bind state to this.props on component which is in iframe?Redux/React - 为什么我不能将状态绑定到 iframe 中组件上的 this.props?
【发布时间】:2016-09-10 03:32:40
【问题描述】:

我的 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 route 为: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组件如下:[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,也就是说它将AnotherView组件链接到iframe的页面。

(4)AnotherView 组件如下:

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: [动作.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 上的 Store 配置:

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)现在我在 Control 组件中输入一些单词。当输入 onChange 会触发 addTodo action 函数在 reducer 中 dispatch action,最终改变 state。一般来说,状态变化会影响Frame组件和AnotherView组件,因为我使用了react-reduxconnect,将state属性绑定到组件上的this.props

但其实AnotherView组件有问题。在 Frame 组件中,console.log 值正确显示您输入的文本。在AnotherView组件中,连connect回调都会被触发(console.log会打印'another view trigger state: ...'),渲染中的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},你可以看到当你输入值时值实时变化控制组件。如果我使用 iframe 将 AnotherView 组件链接为它的页面,您看不到任何更改的 {text} 值,因为我的文本默认值为空字符串值。

当状态改变时,如何将state 属性绑定到iframe 中的组件中的this.props

更新 我无法在 iframe 中获得最新的state(源是 React 组件),当我在另一个组件中更改 state 时,实际上触发了 mapStateToProps!(表示 iframe 源组件)但它的状态不是最新的mapStateToProps 功能。它与react-redux 库无关吗?

这是组件中的最新状态:

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

【问题讨论】:

    标签: reactjs redux universal


    【解决方案1】:

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

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

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

    【讨论】:

    • 感谢您的回复,激励我找到另一种方式来达到我的目标。我必须在 iframe 中渲染,因为我想使用 JSX 作为模板引擎在 iframe 中渲染模板,我不想使用其他模板引擎,如玉、handleBar 等。如果我想同步 iframe 状态数据,是否可以通过 iframe 中的最新状态进行通信?
    • 我不明白 JSX 与 iframe 有什么关系。 :-(
    • 如果您使用react-frame-component 之类的内容,则需要删除&lt;script&gt;。那么running app会使用iframe作为渲染目标,所以不会出现不同步状态的问题。
    • 嗨,Dan,请检查我的总代码github.com/sevenLee/ServerSideRedux.git,,,我使用react组件作为iframe页面(react-router决定),所以没有脚本标签,只是html通过react渲染组件。
    • 我想通过外部组件实时更改 iframe 页面,就像模板生成器应用程序一样。我可以在 iframe 页面上定义自定义样式(如主题),这是我的真实对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-30
    • 2018-12-16
    • 2020-07-11
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 2017-03-31
    相关资源
    最近更新 更多