大家好: 我的前端项目是 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> ); }
}
![]() | 1 bdbai 2016-08-24 13:36:20 +08:00 via Android http://reactjs.cn/react/docs/component-specs.html 你可以自己把 bootgrid 封装成一个子组件,参数从 props 传进去。这样一旦父组件条件变化,子组件就能重新渲染。 |
![]() | 2 simonlify OP @bdbai 你说的这种方法我试过了,好像不行的,父组件中 select 选择,触发事件,生成筛选参数,这时候,我只能把参数存到 state 中,这时候就会重新渲染 html 结构,在这之后才能调用 bootgrid 重新生成分页和操作标签, 问题在于,一旦调用 setState 后,最后的操作肯定是 render() 之后就不会有任何操作了,而我希望, render()之后,在调用 bootgrid ,刷新页面效果,不知道我说清楚了没? |
![]() | 3 bdbai 2016-08-24 16:10:21 +08:00 @simonlify 子组件的 componentDidMount 和 componentDidUpdate 都要调用 bootgrid 。我简单改了一下子组件,你看这样如何。 https://gist.github.com/bdbai/2a195e83fc0fce13d2f9a6aa4d9dac5f |
![]() | 4 ziki 2016-08-24 16:37:43 +08:00 componentDidMount 是只有首次渲染才执行的,那你试下 componentDidUpdate |
![]() | 5 Axighi 2016-08-24 16:45:21 +08:00 componentWillReceiveProps |
![]() | 6 simonlify OP @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( .......... ); } } |
![]() | 7 Axighi 2016-08-24 17:13:37 +08:00 在 changeSelect 里的 setState 中加回调,应该可以。 |
![]() | 8 Axighi 2016-08-24 17:15:11 +08:00 setState({}, () => { //do something}) |
![]() | 9 bdbai 2016-08-24 17:17:42 +08:00 |
![]() | 11 simonlify OP @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 这个事件怎么触发?? |
![]() | 12 simonlify OP |
![]() | 13 serco 2016-08-24 18:18:29 +08:00 @simonlify 你一定哪里写错了,如果你的 changeSelect 确实执行了,肯定也会再执行到 componentDidUpdate. 你暂时可以不去管 shouldComponentUpdate ,那个只是控制 state 或者 props 改变时是否更新,默认是 true |
![]() | 14 bdbai 2016-08-24 19:34:02 +08:00 via Android @simonlify 也许污染的 DNS 被缓存了。 子组件只关心需要显示哪些项目,由父组件从 props 传进去即可。过滤归别的组件( Select1 )管。 一旦父组件发现过滤条件有变,就会把新的筛选出的内容传递给子组件(调用 shouldComponentUpdate ),使子组件重新渲染(调用 componentDidUpdate )。如果 shouldComponentUpdate 发现实际过滤出来的东西没变,就直接返回 false 省去一次多余的渲染。 |
![]() | 15 simonlify OP @serco 这段代码我反复检查了很多遍 ,不会有什么语法错误 changeSelect 如下: changeSelect(obj, sel_val){ alert('a -- '+ sel_val) this.setState({devicesn:sel_val}); } 这段代码执行后,下面就是 render() 然后就没有了,不会走到 componentDidUpdate 中去,我很郁闷!!! |
![]() | 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 设 '',就完事了绕成这样 |
![]() | 17 simonlify OP @bdbai 根据你的建议,我重新改了一下组件结构,父组件包括 Select 和 DeviceList 两个子组件 select 的值 以<DeviceList devicesn={ devicesn } /> 这种方式传入 如果在父组件中用 setState() 确实会重新渲染子组件,问题在于,不管用什么方式,都不会重新调用 bootgrid 也就是说,再次渲染子组件时, componentDidUpdate 是不会执行到的, 难道我漏掉了什么??? |
![]() | 19 bdbai 2016-08-24 21:22:14 +08:00 |
![]() | 20 arslion 2016-08-24 21:22:40 +08:00 是有多喜欢这个 bootgrid … 分页刷新查询选择吧啦吧啦,到底有多难写啊。你貌似已经在这个问题上纠结了一天多了,不如就此打住,延一天的时间自己写组件 对了,不是很懂这种感叹号的用法,一种微咆哮的感觉 ;) |
![]() | 21 simonlify OP @arslion 这个问题确实是让我纠结半天多了,我之所以一直纠结这个问题,不是因为我没有别的解决办法,别的办法随便想想也有好几种。 只是本人刚做这种前端组件框架几个星期而已,很多原理方面的知识都不太清楚,如果碰到了这个问题,即使纠结这么长时间,也是搞清楚了蛮多别的问题,也算是避免以后再次踩类似这样的坑,不算没有收获 |
![]() | 22 simonlify OP @bdbai 还是不麻烦你啦, shouldComponentUpdate 去掉我也试过,没有效果,也许是我还理解的不够深刻吧,不过还是搞明白了很多问题 这个问题呢,我想来想去,没必要再纠结下去了,我换了一种方式去解决了 大概的思路就是, select 获取过滤条件后,不用 setState 去重新渲染子组件列表,我从 bootgrid.js 源码里面下手,修改了一些源码,不管怎么说,这个刷选,我算是搞定了 不过这个问题的本质,确实是没有解决的,我现在项目时间比较急,只能等有空了再来研究这个问题了 谢谢各位耐心的解答,非常感谢! |
![]() | 23 bdbai 2016-08-24 22:08:21 +08:00 via Android @simonlify 最漂亮的方案是自己把 bootgrid.js 用 React 组件重写,然后开源... |
![]() | 25 simonlify OP @bdbai 哈哈,这个...这个... 目前暂不考虑, 分析 bootgrid 就够麻烦的了,还要组件重写,关键是我写 ReactJS ,不到一个月,完全没勇气完成这项壮举,还是等后来者吧。 |
![]() | 26 simonguo 2016-09-03 11:37:03 +08:00 没有你这么做的,既然选择了用 react ,为什么还要用 bootgrid.js 去渲染 table ,建议你不要这样做。 推荐一个 react table 组件 http://rsuite.github.io/rsuite-table/ |
![]() | 27 simonlify OP @simonguo 感谢,这个我也是后来才知道的,之所以用 bootgrid ,是因为这是我用 React 做的第一个项目,很多方面没有规范化,而且这个项目已经势成骑虎,不好在中途换了,也算是累积经验吧,下一个项目肯定不会再出现这种情况了,再次感谢! |