资讯专栏INFORMATION COLUMN

少踩点坑,你值得知道的React事件绑定

Riddler / 928人阅读

摘要:比如在的时候组件挂载的时候会根据指定的参数继续向下执行,则会在挂载的时候还没发生点击事件就直接执行了父组件的函数。即中转函数的效果,保证了时的函数都是并且也防止了子组件在挂载时,顺着调用父组件的函数从而避免了一系列错误。

写在前面

以前写Vue写惯了,心血来潮,写起了react。

github地址:Close2React

项目使用框架版本主要有 react(15.4.1) + react-dom(15.4.1) + webpack(1.13.3) + axios(0.15.3) + node(6.2.2), 点击查看项目简介:一段人人都应该知道的从 vue 到 react 的过渡史

目前该项目有两个分支, half-es6 + master

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 ({text})
    },
    
    render() { ...... }
});

自以为把方法传入了子组件,就在Tab子组件中直接this.props.choose调用父组件的方法

const Tab = React.createClass({
    render(){
        return (
            {this.props.children}
        )
    }
});

结果浏览器打开就爆炸了。boom

大概意思就是说:
我在父组件中的setState在渲染的时候导致了一个错误。React不能更新一个正在变化的state。
组件中的render应该是一个带有state和props的pure function(纯函数)。如果不是纯函数,构造器会产生一些副作用。
比如在render的时候(组件挂载的时候)会根据props指定的参数继续向下执行,则会在挂载的时候(还没发生点击事件)就直接执行了父组件的函数。

顺便解释一下pure function
1、给出同样的参数值,该函数总是求出同样的结果。该函数结果值不依赖任何隐藏信息或程序执行处理可能改变的状态或在程序的两个不同的执行,也不能依赖来自I/O装置的任何外部的输入
2、结果的求值不会促使任何可语义上可观察的副作用或输出,例如易变对象的变化或输出到I/O装置

正确姿势
const Tab = React.createClass({
    chooseTab() { // 子组件的中转函数
        this.props.choose(this.props.idx); //在这里调用父组件的函数
    },
    
    render(){
        return (
            {this.props.children}
        )
    }
});

这个中转函数的名词是我自己取的,只是这样就能让点击事件的函数变成pure function,就不会在组件挂载的时候就沿着props继续向下执行,就能避免在挂载组件的时候就直接调用父组件的setState了。

--

案例2 todolist 的 编辑 & 保存 示例效果图

错误示范
// 父组件
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 (
            
        )
    },
    
    render() { ...... }
});

这里也会和案例1有同样的情况,父组件用props传入的方法里面有setState,如果在子组件的reader中直接用this.props.handleTxtChange 调用的话,会导致函数不纯。

错误姿势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 (
            
  • ... { this.state.status? // 重点代码开始 : // 重点代码结束

    {this.props.text}

    } ...
  • ) } ...

    你会发现,你想要给props的方法里传的自定义参数index能正常获取,
    而框架自带参数event怎么都拿不到,
    结果只能如下,event会变成undefined。

    错误姿势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 (
                
  • ... { this.state.status? // 重点代码开始 : // 不带参数 // 重点代码结束

    {this.props.text}

    } ...
  • ) } ...

    当发现多传了参数,导致了框架自带的默认参数event怎么都取不到的时候,
    决定不传参数,用其他歪门邪道(比如自定义属性)拿到想要的参数。
    在input中输入内容,结果如下。虽然正确,但这样写感觉实在是不够智能。

    总之,这样写虽然解决了问题,但我还是觉得姿势还是不对。

    正确姿势
    // 正确的父组件
    ...
        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 (
                
  • ... { this.state.status? // 重点代码开始 : // 重点代码结束

    {this.props.text}

    } ...
  • ) } ...

    如果这样写的话,是达到了和案例1一样的效果。
    中转函数的效果,保证了render时的函数都是pure function
    并且也防止了子组件在挂载时,render顺着this.props.function调用父组件的函数
    从而避免了一系列错误。

    案例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 (
                
    // 定义了一个ref是addIpt的input标签
    ) } });

    --

    总结

    为了尽可能使用pure function,也为了保证挂载的时候不要出问题
    在子组件需要调用父组件的this.props.function的时候
    尽可能使用中转函数
    你懂得~~

    写在后面

    github地址:Close2React

    我是嘉宝Appian,一个卖萌出家的算法妹纸。

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/86607.html

    相关文章

    • angular性能优化心得

      摘要:本文针对的读者具备性能优化的相关知识雅虎条性能优化原则高性能网站建设指南等拥有实战经验。这种机制能减少浏览器次数,从而提高性能。仅会检查该和它的子,当你确定当前操作仅影响它们时,用可以稍微提升性能。 搬运自: http://atian25.github.io/2014/05/09/angular-performace/ 不知不觉,在项目中用angular已经半年多了,踩了很多坑...

      guqiu 评论0 收藏0
    • Mobx + React Native 获取路由状态信息

      摘要:年前公司由一个项目是使用来开发的所以遇到了一些问题比较影响开发进程的就是路由问题了实际上就是这个组件比较难懂这里给大家讲解一下希望大家少踩点坑另外本篇文章使用的是环境主要讲解的还是如何使用记录中路由的状态但是会穿插一些小内容这里虽然讲到的是 年前公司由一个项目是使用 ReactNative 来开发的所以遇到了一些问题,比较影响开发进程的就是路由问题了,实际上就是 ReactNaviga...

      李涛 评论0 收藏0
    • 滴滴一下,小程序专车来了

      摘要:功能三滴滴费用计算古人云细节决定成败,一个良好的微信小程序往往就是一些细节打动人心,居然是模仿,虽做不到百分百,至少还是很希望一模一样。 最近时常感叹道:时间总是那么的快,转瞬即逝。对于像我这种刚入门的小生来讲,技术每天都在更新,框架也层出不穷,有时候还没弄懂这个知识大牛们又推出了更好的技术。当然学习好的技术也是十分重要的。但是在学习之后怎样才能够得到自己想要的呢,一个好的建议便是静...

      SwordFly 评论0 收藏0
    • 从引入到实例最详尽 vue.js引入vuex储存接口数据并调用流程

      摘要:第二步就是调用,我在文件夹简历了个的文件,方便管理。然后在里面引入至此,安装基本完成,下面介绍这个东西的用法。其实这东西用法简单的一比,就是几个属性而已。例如然后在组件中直接调用这样就直接运行了这个方法。 项目终于做完了,博客也很久没更新了,写下这个项目用到的一些知识,以后大家也少踩点坑第一步当然还是安装了,这里只介绍npm的安装方法,别的请自行百度。 npm install vuex...

      LeviDing 评论0 收藏0
    • 前端每周清单第 41 期 : Node 与 Rust、OpenCV 火花,网络安全二三事

      摘要:的网站仍然使用有漏洞库上周发布了开源社区安全现状报告,发现随着开源社区的日渐活跃,开源代码中包含的安全漏洞以及影响的范围也在不断扩大。与应用安全是流行的服务端框架,本文即是介绍如何使用以及其他的框架来增强应用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清单专注...

      syoya 评论0 收藏0

    发表评论

    0条评论

    Riddler

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <