V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
devwolf
V2EX  ›  React

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

  •  
  •   devwolf ·
    yctjb1 · 2020-06-09 16:59:33 +08:00 · 2142 次点击
    这是一个创建于 1663 天前的主题,其中的信息可能已经有所发展或是发生改变。

    父组件 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 也跟着刷新了

    9 条回复    2020-06-10 13:54:01 +08:00
    doublelam
        1
    doublelam  
       2020-06-09 18:41:58 +08:00 via Android
    不要用匿名函数,重新创建一个子组件,用 react.memo 试试?
    devwolf
        2
    devwolf  
    OP
       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
        3
    devwolf  
    OP
       2020-06-10 08:43:02 +08:00
    这次的尝试曾使我一度怀疑是在父组件 componentDidMount 部署浏览器窗口的尺寸的监听与更新 screenHeight 所导致的(虽然没什么道理,只是在使用了 memo+useState 后比较是否有什么不同时发现——改变浏览器窗口尺寸也会触发 iframe 中内容的刷新,但是在父子组件两处都注释了这个监听器后改变尺寸刷新依旧。this.props.children 看起来同时也规避了这处问题)
    ccraohng
        4
    ccraohng  
       2020-06-10 08:47:13 +08:00 via Android
    错别字看着头疼。你看下那个二级路由循环那里是不是有改变什么
    devwolf
        5
    devwolf  
    OP
       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
        6
    devwolf  
    OP
       2020-06-10 09:18:26 +08:00
    上面 render 有些变量没来得及删,这是个上面安排得给别人擦屁股得项目,看上去上一位为了规避一些 bug 同样使用了一些怪招。笔者修改前,这儿得二级路由还是在用户点击菜单传值时才配置路由,现在采用得法子是登陆后全都配置好(有点好奇。。。笔者觉得这个才是正常思维来着)
    ccraohng
        7
    ccraohng  
       2020-06-10 09:45:02 +08:00 via Android
    可以试试,把箭头函数改成常量值,你可以看下 MsinFrame 是不是在不断加载卸载
    feichao
        8
    feichao  
       2020-06-10 13:11:12 +08:00   ❤️ 1

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

    改成

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

    试下,应该就能避免「父组件重新渲染时子组件 iframe 一直在刷新」的问题了
    devwolf
        9
    devwolf  
    OP
       2020-06-10 13:54:01 +08:00 via Android
    @feichao 非常感谢,render 确实能解决这个问题。
    orz 顺着这个方向我查查去
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2383 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:02 · PVG 00:02 · LAX 08:02 · JFK 11:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.