父组件 state 更新触发渲染导致子组件中 iframe 也跟着刷新,疑惑自己误打误撞的“解决?”办法

2020-06-09 16:59:33 +08:00
 devwolf

父组件 state 更新触发渲染导致子组件中 iframe 也跟着刷新,莫名通过“原本子组件部署在父组件 render 中得 return 处,现将该处替换为 this.props.children,然后子组件对应部署在父组件得部署区得双标签内”来规避了

前半句 [父组件 state 更新触发渲染导致子组件中 iframe 也跟着刷新] 不知各位是否有遇见,
后半句是笔者误打误撞试错出来得“解决?”办法,至少规避了父组件重新渲染时子组件 iframe 一直在刷新.

这会儿想探究一下其中得原因,不知从何入手。
当然如果笔者对现象分析错误——“实际上压根没有上述这回事,导致该效果得实际原因是别的什么”
还请 v 佬们告知。

业务逻辑以及项目结构得话大概描述一下


<Router basename="/" hashType="hashbang">
        <Switch>
         <Route path="/main" component={()=><LayoutContainer>
         <ChildRoute/></LayoutContainer>}/>
        </Switch>
      </Router>

//LayoutContainer 里是一堆布局,挑个合适得地方塞上{this.props.children}

然后 ChildRoute 里就是二级路由以及 iframe 的配置

<Route exact path={'/main/'+item.part} key={item.all} component={()=><iframe name="mainFrame"
                style={{width: '100%',
                height: screenHeight,
                border:'0px'}}
                onLoad={()=>this.setState({loading:false})}
                sandbox="allow-same-origin allow-scripts allow-forms allow-top-navigation
                allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox
                allow-presentation"
                src ={fullsrc}></iframe>/>}/>

流程就是,点击布局页面切换了登陆用户得身份会更新 state 导致 LayoutContainer 重新渲染,若不采用 this.props.children 得法子将导致其“子组件”得 iframe 也跟着刷新了

1954 次点击
所在节点    React
9 条回复
doublelam
2020-06-09 18:41:58 +08:00
不要用匿名函数,重新创建一个子组件,用 react.memo 试试?
devwolf
2020-06-10 08:24:19 +08:00
@doublelam

```
import React, {Component ,Fragment,useState} from "react";

import {Spin} from 'antd';

const MainFrame = React.memo(props => {



const [loading, updateLoading] = useState(true);
const [screenHeight, updateScreenHeight] = useState(document.documentElement.clientHeight-70-64);



//这里获取的高度为 iframe 服务
//window.addEventListener("resize",()=>{
// const screenHeight = document.documentElement.clientHeight;
// updateScreenHeight(screenHeight-70-64)
//});//获得窗口高度




const fullsrc = props.fullsrc

return (<Fragment>
<Spin tip= "加载中请稍后..." spinning={loading}>
<iframe name="mainFrame"
style={{width: '100%',
height: screenHeight,
border:'0px'}}
onLoad={()=>updateLoading(false)}
sandbox="allow-same-origin allow-scripts allow-forms allow-top-navigation
allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox
allow-presentation"
// scrolling="auto"
src ={fullsrc}></iframe>
</Spin>

</Fragment>)

});

export default MainFrame;
```
我尝试使用了 memo + useState,看起来 iframe 依旧在父组件切换身份而触发重渲染时疯狂刷新
devwolf
2020-06-10 08:43:02 +08:00
这次的尝试曾使我一度怀疑是在父组件 componentDidMount 部署浏览器窗口的尺寸的监听与更新 screenHeight 所导致的(虽然没什么道理,只是在使用了 memo+useState 后比较是否有什么不同时发现——改变浏览器窗口尺寸也会触发 iframe 中内容的刷新,但是在父子组件两处都注释了这个监听器后改变尺寸刷新依旧。this.props.children 看起来同时也规避了这处问题)
ccraohng
2020-06-10 08:47:13 +08:00
错别字看着头疼。你看下那个二级路由循环那里是不是有改变什么
devwolf
2020-06-10 09:13:32 +08:00
@ccraohng

那儿也就一个配置路由的操作来着
```
import React,{Component} from "react";
import {Switch,Route,Redirect,withRouter} from 'mirrorx';

import HomeContainer from "Pages/Home/HomeContainer"
import PerconContainer from "Pages/Person/personContainer"
import MainFrame from "./MainFrame"

class ChildRoute extends Component{
constructor(props){
super(props);

this.state={
pathname_list:[]
}
}
componentWillMount(){

let pathname_list = JSON.parse(sessionStorage.getItem("url_routes"));
if(pathname_list){
// console.log("有了有了")
this.setState({pathname_list})
}else{
// console.log("我没有")
}



}

render(){
const redirectUrl = JSON.parse(sessionStorage.getItem("url"));
const userInfo = sessionStorage.getItem("userInfo");
const user = sessionStorage.getItem("user");
const url = "/main/"+redirectUrl;
const {match} = this.props;
const token = sessionStorage.getItem("TOKEN");
const src = redirectUrl+"?user="+user+"&token="+token;



return(
token==null?(
<Redirect to="/login"/>
) :
(
<Switch>
<Route exact path={'/main'} component={HomeContainer} breadcrumbName="首页" />
<Route exact path={'/main/person'} component={PerconContainer} />

{this.state.pathname_list.length!==0&&this.state.pathname_list.map(item=>
<Route exact path={'/main/'+item.part} key={item.all} component={()=><MainFrame fullsrc={item.all+"?user="+user+"&token="+token}/>}/>
)}

</Switch>
)
)
}
}
export default withRouter(ChildRoute)

```
devwolf
2020-06-10 09:18:26 +08:00
上面 render 有些变量没来得及删,这是个上面安排得给别人擦屁股得项目,看上去上一位为了规避一些 bug 同样使用了一些怪招。笔者修改前,这儿得二级路由还是在用户点击菜单传值时才配置路由,现在采用得法子是登陆后全都配置好(有点好奇。。。笔者觉得这个才是正常思维来着)
ccraohng
2020-06-10 09:45:02 +08:00
可以试试,把箭头函数改成常量值,你可以看下 MsinFrame 是不是在不断加载卸载
feichao
2020-06-10 13:11:12 +08:00

```
<Route exact path={...} key={item.all} component={()=><iframe ......
```

改成

```
<Route exact path={...} key={item.all} render={()=><iframe ......
```

试下,应该就能避免「父组件重新渲染时子组件 iframe 一直在刷新」的问题了
devwolf
2020-06-10 13:54:01 +08:00
@feichao 非常感谢,render 确实能解决这个问题。
orz 顺着这个方向我查查去

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/680071

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX