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

2016-08-24 12:16:47 +08:00
 simonlify

大家好: 我的前端项目是 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>
    );
}

}

7905 次点击
所在节点    React
27 条回复
bdbai
2016-08-24 13:36:20 +08:00
http://reactjs.cn/react/docs/component-specs.html

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

https://gist.github.com/bdbai/2a195e83fc0fce13d2f9a6aa4d9dac5f
ziki
2016-08-24 16:37:43 +08:00
componentDidMount 是只有首次渲染才执行的,那你试下 componentDidUpdate
Axighi
2016-08-24 16:45:21 +08:00
componentWillReceiveProps
simonlify
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
2016-08-24 17:13:37 +08:00
在 changeSelect 里的 setState 中加回调,应该可以。
Axighi
2016-08-24 17:15:11 +08:00
setState({}, () => { //do something})
bdbai
2016-08-24 17:17:42 +08:00
@simonlify 翻墙乃程序员必备技能,再说 Gist 也没被墙。
http://paste.ubuntu.com/23084405/
simonlify
2016-08-24 17:56:15 +08:00
@bdbai 亲,我的 vpn 账号可是付费的,不要怀疑我不会翻墙!!刚才确实没打开!这个连接看到了
simonlify
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
2016-08-24 18:15:23 +08:00
@Axighi 我有这样写,但是没有效果,选择过滤条件,貌似没办法触发回调??
代码如下:
this.setState({devicesn:sel_val}, ()=>{
alert('e')
});
serco
2016-08-24 18:18:29 +08:00
@simonlify 你一定哪里写错了,如果你的 changeSelect 确实执行了,肯定也会再执行到 componentDidUpdate. 你暂时可以不去管 shouldComponentUpdate ,那个只是控制 state 或者 props 改变时是否更新,默认是 true
bdbai
2016-08-24 19:34:02 +08:00
@simonlify 也许污染的 DNS 被缓存了。

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

一旦父组件发现过滤条件有变,就会把新的筛选出的内容传递给子组件(调用 shouldComponentUpdate ),使子组件重新渲染(调用 componentDidUpdate )。如果 shouldComponentUpdate 发现实际过滤出来的东西没变,就直接返回 false 省去一次多余的渲染。
simonlify
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
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
2016-08-24 21:04:36 +08:00
@bdbai 根据你的建议,我重新改了一下组件结构,父组件包括 Select 和 DeviceList 两个子组件
select 的值 以<DeviceList devicesn={ devicesn } /> 这种方式传入
如果在父组件中用 setState() 确实会重新渲染子组件,问题在于,不管用什么方式,都不会重新调用 bootgrid
也就是说,再次渲染子组件时, componentDidUpdate 是不会执行到的,
难道我漏掉了什么???
simonlify
2016-08-24 21:06:04 +08:00
@ianva React 方面是新手,见谅!!
bdbai
2016-08-24 21:22:14 +08:00
@simonlify 把 shouldComponentUpdate 去掉试试?

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

对了,不是很懂这种感叹号的用法,一种微咆哮的感觉 ;)

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

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

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

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

© 2021 V2EX