少踩点坑,你值得知道的React事件绑定
写在前面以前写Vue写惯了,心血来潮,写起了react。 github地址:Close2React 项目使用框架版本主要有 目前该项目有两个分支, half-es6和master实现的功能一样,实现了CURD + Axios + Others half-es6的写法并没有完全使用es6的class的概念,master是完善了它 现在让我们快速了解React的事件绑定都有什么坑~ 案例1:tab示例效果图
错误示范// 父组件主要是为了实现tab的切换 const Content = React.createClass({ getInitialState() { return { tabTxt: ['CURD','Axios','Others'],choice: 0,//目前激活的tab的下标 } },switchChoice(idx){ // 设置choice this.setState({ choice: idx }) },renderTabInit(text,idx) { return (<Tab key={idx} idx={idx} // 利用props的绑定,把switchChoice传到子组件中去,this.props.choose可以调用到这个方法 choose={this.switchChoice} choice={this.state.choice} >{text}</Tab>) },render() { ...... } }); 自以为把方法传入了子组件,就在Tab子组件中直接this.props.choose调用父组件的方法 const Tab = React.createClass({ render(){ return ( <span className={this.props.idx == this.props.choice? "tab on" : "tab"} data-idx={this.props.idx} // 本意是在点击的时候,能够直接调用父组件的方法 onClick={this.props.choose(this.props.idx)} >{this.props.children}</span> ) } }); 结果浏览器打开就爆炸了。boom
正确姿势const Tab = React.createClass({ chooseTab() { // 子组件的中转函数 this.props.choose(this.props.idx); //在这里调用父组件的函数 },render(){ return ( <span className={this.props.idx == this.props.choice? "tab on" : "tab"} data-idx={this.props.idx} onClick={this.chooseTab} // 调用中转函数 >{this.props.children}</span> ) } });
案例2todolist 的 编辑 & 保存 示例效果图
错误示范// 父组件 const PageA = React.createClass({ getInitialState() { ... },// 初始化todolist的数据 componentDidMount(){ ... },// 挂载组件时的函数 initDidCount() { ... },// 更新完成的进度 handleTxtChange(event){ // 重点: 当input的输入值变化时调用这个函数 let index = event.target.getAttribute('data-index'); // 强行得到todolist的index // 这里一定需要index这个参数作为修改this.state.list时候的下标 this.state.list[index].text = event.target.value; // 把input的值更新到state上去 this.setState({ list: this.state.list }); this.initDidCount(); // 更新完成进度 },handleCheckChange(event,idx) { ... },// checkbox的onChange,和input的onChange一样 deleteItem(idx) { ... },// 删除 initListLi(val,idx) { return ( <List {...val} key={idx} index={idx} // 绑定一些需要用到的props // 利用props的绑定,把handleTxtChange传到子组件中去, //子组件中用this.props.handleTxtChange可以调用到这个方法 //(handleCheckChange也是同理) handleTxtChange={this.handleTxtChange} handleCheckChange={this.handleCheckChange} deleteItem={this.deleteItem} /> ) },render() { ...... } }); 这里也会和案例1有同样的情况,父组件用props传入的方法里面有setState,如果在子组件的reader中直接用 错误姿势1// 错误的父组件1 ... handleTxtChange(event,idx){ // 重点:【错误写法1】 强行传了两个参数 console.log(event,idx); // 在控制台上输出结果 this.state.list[idx].text = event.target.value; // 把input的值更新到state上去 this.setState({ list: this.state.list }); this.initDidCount(); // 更新完成进度 },... // 错误的子组件1 ... render (){ return ( <li className="li"> ... { this.state.status? // 重点代码开始 <input type="text" className="ipt" defaultValue={this.props.text} //【错误写法1】 直接调用了父组件的函数,并且直接传了两个参数(框架的默认参数event和自定义参数index) onChange={this.props.handleTxtChange(event,this.props.index)}/>: // 重点代码结束 <p className="p">{this.props.text}</p> } ... </li> ) } ... 你会发现,你想要给props的方法里传的自定义参数index能正常获取, 错误姿势2// 错误的父组件2 ... handleTxtChange(event){ // 重点:【错误写法2】 只有框架自带参数event console.log(event.target); // 在控制台上输出结果 let index = event.target.getAttribute('data-index'); // 强行拿到标签上的自定义属性 this.state.list[index].text = event.target.value; // 把input的值更新到state上去 this.setState({ list: this.state.list }); this.initDidCount(); // 更新完成进度 },... // 错误的子组件2 ... render (){ return ( <li className="li"> ... { this.state.status? // 重点代码开始 <input type="text" className="ipt" defaultValue={this.props.text} //【错误写法2】 直接调用父组件的函数,但是不传参数 // 自定义参数利用自定义属性的方式传入, // 这次尝试,也只是为了能够拿到正确的event data-index={this.props.index} // 强行使用了自定义属性 onChange={this.props.handleTxtChange}/>: // 不带参数 // 重点代码结束 <p className="p">{this.props.text}</p> } ... </li> ) } ... 当发现多传了参数,导致了框架自带的默认参数event怎么都取不到的时候, 正确姿势// 正确的父组件 ... handleTxtChange(event,idx){// 重点:【正确姿势】 不仅带了框架默认参数event,还带了自定义参数 this.state.list[idx].text = event.target.value; this.setState({ // 最正常的赋值写法 list: this.state.list }); this.initDidCount(); },... // 正确的子组件 ... handleTxt(event) { // 用一个中转函数来存onChange时会调用的父组件的函数 // 并加上任意的参数 this.props.handleTxtChange(event,this.props.index); },render (){ return ( <li className="li"> ... { this.state.status? // 重点代码开始 <input type="text" className="ipt" defaultValue={this.props.text} // 【正确姿势】调用子组件的中转函数 onChange={this.handleTxt}/>: // 重点代码结束 <p className="p">{this.props.text}</p> } ... </li> ) } ...
案例3案例3纯粹是为了演示一个增加操作,在增加一条记录后,需要清空input的内容时踩的坑 // 父组件 addLiItem(obj) { this.state.list.push(obj); // 没啥好说,就是添加一个元素到list中去 this.setState({ list: this.state.list }); this.initDidCount(); }, // 子组件 const Add = React.createClass({ getInitialState() { return { addValue: '',addStatus: false } },handleAddChange(event) { this.setState({ addValue: event.target.value }) },add(){ this.props.addLiItem({ text: this.state.addValue,status: false }); this.setState({ //【重点部分】 addValue: '' },()=>{ this.refs.addIpt.value = ''; // 利用ref操作dom }); },// 如果只是setState的时候发现完成没办法达到清空的效果 // 这时候的【正确姿势】是去操作dom,一定要操作dom render() { return ( <div> // 定义了一个ref是addIpt的input标签 <input className="ipt" onChange={this.handleAddChange} value={this.addStatus} ref="addIpt"/> <button className="btn btn-save" style={{float: 'left'}} onClick={this.add}>添加</button> </div> ) } });
总结为了尽可能使用pure function,也为了保证挂载的时候不要出问题 写在后面
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 正则表达式 – 我应该避免正则表达式吗?
- 结构体
- c# – 如果在“using”语句中使用try / catch,是否会处置一
- Flex ActionScript程序员各个阶段描述,大家可以对号入座
- Vb.net 利用数组进行组合查询
- 验证方法 ValidationExpression 正则表达式
- 域驱动设计 – DDD和Getters and Setters的使用
- ruby-on-rails – 未定义的方法skip_confirmation! – 设计
- haskell – 如何告诉cabal指定我的程序依赖项之一的依赖项?
- XML解析乱码,UnmarshalException和 SAXParseException