关于 ReactJS+ES6 组件开发 重复渲染问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
simonlify
V2EX    React

关于 ReactJS+ES6 组件开发 重复渲染问题

  •  
  •   simonlify 2016-08-24 12:16:47 +08:00 8211 次点击
    这是一个创建于 3336 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大家好: 我的前端项目是 React+es6+redux+webpack 结构

    功能很简单,获取数据,渲染列表,问题在于,我把列表渲染完之后,需要调一个 bootgrid 的 jQuery 库,去生成分页和自定义操作的界面操作和效果,那么,在初次加载的时候,没有问题,但是在做 select 筛选的时候, componentDidMount 里面的没办法再次调用 bootgrid 的 jQuery 库,这样就导致了重新渲染的列表没办法展示其效果。我琢磨了很长时间,没找到解决办法。 请问各位,是否有办法解决这个问题??? 在线等! 简单代码如下: class ClientsHistory extends Component { constructor(props) { super(props); this.state = {list: []}; } componentDidMount(){ let that = this; $("#tb-grid-data").bootgrid(); let grid = $("#tb-grid-devicelist").bootgrid({ formatters:{ "operation":function(column , row){ return 'aaa'; } } }); } changeSelect(sel_val){ this.setState({devicesn:sel_val}); // $("#tb-grid-devicelist").bootgrid('reload'); } render(){ let devicelist = GetLocalData(device_data_key); var rows = []; if(devicelist != null){ var sn = this.state.devicesn; devicelist.forEach(function(result, index){ if(typeof sn != 'undefined' && sn != ''){ if(result.sn == sn){ rows.push( <ClientRows rowdata={result} key={index} /> ); } }else{ rows.push( <ClientRows rowdata={result} key={index} /> ); } }); } return( <div className="card z-depth-1"> <div className="card-header"> <div className="div-tips-left"> <Select1 changeselect={this.changeSelect.bind(this)}/> </div> </div> <div className="table-responsive"> <table id="tb-grid-devicelist" className="table table-striped"> <thead> <tr> <th data-column-id="sn">aa</th> <th data-column-id="uptime">bb</th> </tr> </thead> <tbody> {rows} </tbody> </table> </div> </div> ); } 

    }

    27 条回复    2016-09-03 18:10:43 +08:00
    bdbai
        1
    bdbai  
       2016-08-24 13:36:20 +08:00 via Android
    http://reactjs.cn/react/docs/component-specs.html

    你可以自己把 bootgrid 封装成一个子组件,参数从 props 传进去。这样一旦父组件条件变化,子组件就能重新渲染。
    simonlify
        2
    simonlify  
    OP
       2016-08-24 14:15:51 +08:00
    @bdbai 你说的这种方法我试过了,好像不行的,父组件中 select 选择,触发事件,生成筛选参数,这时候,我只能把参数存到 state 中,这时候就会重新渲染 html 结构,在这之后才能调用 bootgrid 重新生成分页和操作标签,
    问题在于,一旦调用 setState 后,最后的操作肯定是 render() 之后就不会有任何操作了,而我希望, render()之后,在调用 bootgrid ,刷新页面效果,不知道我说清楚了没?
    bdbai
        3
    bdbai  
       2016-08-24 16:10:21 +08:00
    @simonlify 子组件的 componentDidMount 和 componentDidUpdate 都要调用 bootgrid 。我简单改了一下子组件,你看这样如何。

    https://gist.github.com/bdbai/2a195e83fc0fce13d2f9a6aa4d9dac5f
    ziki
        4
    ziki  
       2016-08-24 16:37:43 +08:00
    componentDidMount 是只有首次渲染才执行的,那你试下 componentDidUpdate
    Axighi
        5
    Axighi  
       2016-08-24 16:45:21 +08:00
    componentWillReceiveProps
    simonlify
        6
    simonlify  
    OP
       2016-08-24 17:03:15 +08:00
    @bdbai 你的代码,我没看到,不过我加了 componentDidUpdate ,不管用啊

    @ziki 我加过了,没用

    @Axighi componentWillReceiveProps 这个我也加过,没用,
    我贴一下,我修改过的代码 , 我打过 alert , b 和 c 都不会执行,

    class Exam extends Component{
    .......

    componentDidMount(){
    let grid = $("#tb-grid-devicelist").bootgrid({
    formatters:{
    "operation":function(column , row){
    return 'aaa';
    }
    }
    }).on("loaded.rs.jquery.bootgrid", function(){
    grid.find(".devicelist-operation").on("change", function(e){
    alert("You select on row: " + $(this).data("row-sn"));
    });
    });
    }
    componentDidUpdate(){
    // this.refs.exam.onselect();
    alert('b')
    let grid = $("#tb-grid-devicelist").bootgrid({
    formatters:{
    "operation":function(column , row){
    return 'aaa';
    }
    }
    }).on("loaded.rs.jquery.bootgrid", function(){
    grid.find(".devicelist-operation").on("change", function(e){
    alert("You select on row: " + $(this).data("row-sn"));
    });
    });
    }
    componentWillReceiveProps(){
    alert('c')
    let grid = $("#tb-grid-devicelist").bootgrid({
    formatters:{
    "operation":function(column , row){
    return 'aaa';
    }
    }
    }).on("loaded.rs.jquery.bootgrid", function(){
    grid.find(".devicelist-operation").on("change", function(e){
    alert("You select on row: " + $(this).data("row-sn"));
    });
    });
    }
    changeSelect(obj, sel_val){
    alert('a -- '+ sel_val)
    this.setState({devicesn:sel_val});
    // $("#tb-grid-devicelist").bootgrid('reload');

    }
    render(){
    return(
    ..........
    );
    }

    }
    Axighi
        7
    Axighi  
       2016-08-24 17:13:37 +08:00
    在 changeSelect 里的 setState 中加回调,应该可以。
    Axighi
        8
    Axighi  
       2016-08-24 17:15:11 +08:00
    setState({}, () => { //do something})
    bdbai
        9
    bdbai  
       2016-08-24 17:17:42 +08:00
    @simonlify 翻墙乃程序员必备技能,再说 Gist 也没被墙。
    http://paste.ubuntu.com/23084405/
    simonlify
        10
    simonlify  
    OP
       2016-08-24 17:56:15 +08:00
    @bdbai 亲,我的 vpn 账号可是付费的,不要怀疑我不会翻墙!!刚才确实没打开!这个连接看到了
    simonlify
        11
    simonlify  
    OP
       2016-08-24 18:14:09 +08:00
    @bdbai 看过你的代码了,有些疑问,我的 render() 中有一个 select 的过滤条件:代码如下
    <div className="card-header">
    <div className="div-tips-left">
    <Select1 changeselect={this.changeSelect.bind(this)}/>
    </div>
    <div className="div-clear"></div>
    </div>

    我一般是触发 select 的时候,获取过滤条件,然后在 changeSelect()里面去 setState()条件,然后 render
    你的代码中有个 shouldComponentUpdate 这个事件怎么触发??
    simonlify
        12
    simonlify  
    OP
       2016-08-24 18:15:23 +08:00
    @Axighi 我有这样写,但是没有效果,选择过滤条件,貌似没办法触发回调??
    代码如下:
    this.setState({devicesn:sel_val}, ()=>{
    alert('e')
    });
    serco
        13
    serco  
       2016-08-24 18:18:29 +08:00
    @simonlify 你一定哪里写错了,如果你的 changeSelect 确实执行了,肯定也会再执行到 componentDidUpdate. 你暂时可以不去管 shouldComponentUpdate ,那个只是控制 state 或者 props 改变时是否更新,默认是 true
    bdbai
        14
    bdbai  
       2016-08-24 19:34:02 +08:00 via Android
    @simonlify 也许污染的 DNS 被缓存了。

    子组件只关心需要显示哪些项目,由父组件从 props 传进去即可。过滤归别的组件( Select1 )管。

    一旦父组件发现过滤条件有变,就会把新的筛选出的内容传递给子组件(调用 shouldComponentUpdate ),使子组件重新渲染(调用 componentDidUpdate )。如果 shouldComponentUpdate 发现实际过滤出来的东西没变,就直接返回 false 省去一次多余的渲染。
    simonlify
        15
    simonlify  
    OP
       2016-08-24 19:40:13 +08:00
    @serco 这段代码我反复检查了很多遍 ,不会有什么语法错误
    changeSelect 如下:
    changeSelect(obj, sel_val){
    alert('a -- '+ sel_val)
    this.setState({devicesn:sel_val});
    }
    这段代码执行后,下面就是 render() 然后就没有了,不会走到 componentDidUpdate 中去,我很郁闷!!!
    ianva
        16
    ianva  
       2016-08-24 20:38:57 +08:00
    LZ 这个 render 逻辑写的真是,就这么简单的逻辑非要绕成看不懂

    ```
    devicelist && devicelist.map((result,index)=>
    result.sn === sn ? <ClientRows rowdata={result} key={index} /> : null)
    ```

    之前设置个默认的 getInitialState devicesn 设 '',就完事了绕成这样
    simonlify
        17
    simonlify  
    OP
       2016-08-24 21:04:36 +08:00
    @bdbai 根据你的建议,我重新改了一下组件结构,父组件包括 Select 和 DeviceList 两个子组件
    select 的值 以<DeviceList devicesn={ devicesn } /> 这种方式传入
    如果在父组件中用 setState() 确实会重新渲染子组件,问题在于,不管用什么方式,都不会重新调用 bootgrid
    也就是说,再次渲染子组件时, componentDidUpdate 是不会执行到的,
    难道我漏掉了什么???
    simonlify
        18
    simonlify  
    OP
       2016-08-24 21:06:04 +08:00
    @ianva React 方面是新手,见谅!!
    bdbai
        19
    bdbai  
       2016-08-24 21:22:14 +08:00
    @simonlify 把 shouldComponentUpdate 去掉试试?

    你有完整的代码和 mock 数据嘛,我来跑跑看。
    arslion
        20
    arslion  
       2016-08-24 21:22:40 +08:00
    是有多喜欢这个 bootgrid …
    分页刷新查询选择吧啦吧啦,到底有多难写啊。你貌似已经在这个问题上纠结了一天多了,不如就此打住,延一天的时间自己写组件

    对了,不是很懂这种感叹号的用法,一种微咆哮的感觉 ;)
    simonlify
        21
    simonlify  
    OP
       2016-08-24 21:53:23 +08:00
    @arslion 这个问题确实是让我纠结半天多了,我之所以一直纠结这个问题,不是因为我没有别的解决办法,别的办法随便想想也有好几种。
    只是本人刚做这种前端组件框架几个星期而已,很多原理方面的知识都不太清楚,如果碰到了这个问题,即使纠结这么长时间,也是搞清楚了蛮多别的问题,也算是避免以后再次踩类似这样的坑,不算没有收获
    simonlify
        22
    simonlify  
    OP
       2016-08-24 22:01:02 +08:00
    @bdbai 还是不麻烦你啦, shouldComponentUpdate 去掉我也试过,没有效果,也许是我还理解的不够深刻吧,不过还是搞明白了很多问题
    这个问题呢,我想来想去,没必要再纠结下去了,我换了一种方式去解决了
    大概的思路就是, select 获取过滤条件后,不用 setState 去重新渲染子组件列表,我从 bootgrid.js 源码里面下手,修改了一些源码,不管怎么说,这个刷选,我算是搞定了
    不过这个问题的本质,确实是没有解决的,我现在项目时间比较急,只能等有空了再来研究这个问题了
    谢谢各位耐心的解答,非常感谢!
    bdbai
        23
    bdbai  
       2016-08-24 22:08:21 +08:00 via Android
    @simonlify 最漂亮的方案是自己把 bootgrid.js 用 React 组件重写,然后开源...
    ianva
        24
    ianva  
       2016-08-25 00:48:11 +08:00
    @simonlify 不是库的使用问题,是编程本身对于圈复杂度的控制
    simonlify
        25
    simonlify  
    OP
       2016-08-25 09:59:15 +08:00
    @bdbai 哈哈,这个...这个... 目前暂不考虑, 分析 bootgrid 就够麻烦的了,还要组件重写,关键是我写 ReactJS ,不到一个月,完全没勇气完成这项壮举,还是等后来者吧。
    simonguo
        26
    simonguo  
       2016-09-03 11:37:03 +08:00
    没有你这么做的,既然选择了用 react ,为什么还要用 bootgrid.js 去渲染 table ,建议你不要这样做。 推荐一个 react table 组件 http://rsuite.github.io/rsuite-table/
    simonlify
        27
    simonlify  
    OP
       2016-09-03 18:10:43 +08:00
    @simonguo 感谢,这个我也是后来才知道的,之所以用 bootgrid ,是因为这是我用 React 做的第一个项目,很多方面没有规范化,而且这个项目已经势成骑虎,不好在中途换了,也算是累积经验吧,下一个项目肯定不会再出现这种情况了,再次感谢!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3083 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 00:35 PVG 08:35 LAX 17:35 JFK 20:35
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86