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.log
是undefined
,比如:
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 这样的库。或者,更简单地说,您可以根本不使用框架,因为不清楚它们的用途。通常人们希望他们隔离一个应用程序实例,但这似乎与您想要的相反。
我的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.log
是undefined
,比如:
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 这样的库。或者,更简单地说,您可以根本不使用框架,因为不清楚它们的用途。通常人们希望他们隔离一个应用程序实例,但这似乎与您想要的相反。