b站React禹哥版视频笔记-React面向组件编程(下)

前言

上一篇:React面向组件编程(上)
本篇内容:React面向组件编程(下)

一、非受控组件与受控组件

收集表单数据:
需求:定义一个包含表单的组件,输入用户名密码后, 点击登录弹出输入的用户名及密码

1.非受控组件

现用现取: 表单中所有输入类DOM(input框、单选、多选等)

class Login extends React.Component{

        handleSubmit=(event)=>{
            event.preventDefault(); // 阻止默认事件————表单提交
            const {username,password}=this;
            alert(`你输入的用户名是${username.value},你输入的密码是${password.value}`);
        }
        render(){
            // { /* <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}> */ }
            return(
                <form onSubmit={this.handleSubmit}>
                用户名:<input ref={c => this.username=c} type="text" name="username"/><br/>
                密码:<input ref={c => this.password=c} type="password" name="password"/><br/>
                <button>登录</button>
                </form>
            )
        }
    }

2.受控组件

  • 随着输入来维护状态,想到Vue中的双向数据绑定

  • 推荐使用受控组件,因为它可以省略掉ref

class Login extends React.Component{
        // 初始化状态
        state={
            username:'',
            password:''
        }

        // 保存用户名到状态中
        saveUsername=(event)=>{
            // console.log(event.target.value);
            this.setState({username:event.target.value});
        }

        // 保存密码到状态中
        savePassword=(event)=>{
            // console.log(event.target.value);
            this.setState({password:event.target.value});
        }

        // 表单提交的回调
        handleSubmit=(event)=>{
            event.preventDefault(); // 阻止默认事件————表单提交
            const {username,password}=this.state;
            alert(`你输入的用户名是${username},你输入的密码是${password}`);
        }

        render(){
            // { /* <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}> */ }
            return(
                <form onSubmit={this.handleSubmit}>
                用户名:<input onChange={this.saveUsername} type="text" name="username"/><br/>
                密码:<input onChange={this.savePassword}  type="password" name="password"/><br/>
                <button>登录</button>
                </form>
            )
        }
    }

二、高阶函数与函数柯里化

高阶函数:
如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
1. 函数接收的参数是一个函数
2. 函数调用的返回值依然是一个函数
常见的高阶函数有:Promise、setTimeout、arr.map()等
函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
函数柯里化示例:

function sum(a){
            return(b)=>{
                return(c)=>{
                    return a+b+c;
                }
            }
        }
const result=sum(1)(2)(3);

当我们要提交的数据不止用户名密码时,比如还有姓名、年龄、地址、电话等等,我们需要写很多个函数来提交信息,很臃肿。而利用函数柯里化,我们写一个函数就行。

class Login extends React.Component{
        // 初始化状态
        state={
            username:'',
            password:''
        }

        // 保存表单数据到状态中
        saveFormData=(dataType)=>{  // 此处saveFormData就是高阶函数
            //console.log('##',dataType);
            return (event)=>{  // 此处的return才是onChange的回调
                //console.log(dataType,event.target.value);
                this.setState({[dataType]:event.target.value});
            }
        }

        // 表单提交的回调
        handleSubmit=(event)=>{
            event.preventDefault(); // 阻止默认事件————表单提交
            const {username,password}=this.state;
            alert(`你输入的用户名是${username},你输入的密码是${password}`);
        }

        render(){
            return(
                // onChange={this.saveFormData('uaername')}    把saveFormData()的返回值作为onChange的回调,而其返回值是undefined
                <form onSubmit={this.handleSubmit}>
                用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/><br/>
                密码:<input onChange={this.saveFormData('password')}  type="password" name="password"/><br/>
                <button>登录</button>
                </form>
            )
        }
    }

三、不用柯里化的写法

用户名:<input onChange={event => this.saveFormData('username',event)} type="text" name="username"/><br/>
密码:<input onChange={event => this.saveFormData('password',event)}  type="password" name="password"/><br/>
// 保存表单数据到状态中
saveFormData=(dataType,event)=>{  
    this.setState({[dataType]:event.target.value});
}

四、组件的生命周期

1.引出生命周期

需求:定义组件实现以下功能:

  1. 让指定的文本做显示 / 隐藏的渐变动画
  2. 从完全可见,到彻底消失,耗时2S
  3. 点击“不活了”按钮从界面中卸载组件
class Life extends React.Component{

        // 页面有更新 状态中的数据驱动着页面显示
        state={opacity:1}

        death=()=>{
            // // 清除定时器
            // clearInterval(this.timer);
            
            // 卸载组件
            ReactDOM.unmountComponentAtNode(document.getElementById('test'));
        }

        // 组件挂载完毕 (只执行一次)
        componentDidMount(){
            this.timer = setInterval(()=>{
                let {opacity}=this.state;
                opacity-=0.1;
                if(opacity<=0) opacity=1;
                // 设置新的透明度
                this.setState({opacity})
            },200)
        }

        // 组件将要卸载
        componentWillUnmount(){
            // 清除定时器
            clearInterval(this.timer);
        }

        // render调用时机:初始化渲染、状态更新之后 
        // 若把定时器放在render中,会造成死循环 cpu温度飙升 render调用次数指数式上升
        render(){
            return(
                <div>
                    <h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>    
                    <button onClick={this.death}>不活了</button>
                </div>
            )
        }
    }

生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子

2.生命周期(旧)_组件挂载流程

-->红色框中是组件挂载流程
class Add extends React.Component{
        constructor(props){
            console.log('Add---constructor');
            super(props);
            // 初始化状态
            this.state={count:0};
        }


        // 加1按钮的回调
        add=()=>{
            // 获取原状态
            const {count}=this.state;
            // 更新状态
            this.setState({count:count+1});
        }

        //组件将要挂载的钩子
        componentWillMount(){
            console.log('Add---componentWillMount');
        }

        // 组件挂载完毕的钩子
        componentDidMount(){
            console.log('Add---componentDidMount');
        }
        
        render(){
            console.log('Add---render');
            const {count}=this.state;
            return (
                <div>
                    <h2>当前求和为:{count}</h2>    
                    <button onClick={this.add}>点我加1</button>
                </div>
            )
        }
    }

控制台输出顺序:

3.生命周期(旧)_setState流程

正常更新过程

		// 控制组件更新的“阀门”
        // 若不写,则底层会自动补,且默认返回值为true
        // 若写了,则必须有返回值,否则有警告
        shouldComponentUpdate(){
            console.log('Add---shouldComponentUpdate');
            return true;
        }

        // 组件将要更新的钩子
        componentWillUpdate(){
            console.log('Add---componentWillUpdate');
        }

        // 组件更新完毕的钩子
        componentDidUpdate(){
            console.log('Add---componentDidUpdate');
        }

4.生命周期(旧)_froceUpdate流程

强制更新:没对状态作出任何的更改,但想更新

<button onClick={this.force}>不更改状态中任何数据,强制更新一下</button>
	// 强制更新按钮的回调
       force=()=>{
           this.forceUpdate();
       }

5.生命周期(旧)_父组件render流程

父组件—A

class A extends React.Component{
        // 初始化状态
        state={carName:'奔驰'}

        changeCar=()=>{
            this.setState({carName:'宝马'})
        }
        render(){
            return(
                <div>
                <div>我是A组件</div>
                <button onClick={this.changeCar}>换车</button>
                <B carName={this.state.carName}/>
                </div>
            )
        }
    }

子组件—B

    class B extends React.Component{
        // 组件将要接收新的props的钩子 (只有父组件再次render时才会调用)
        componentWillReceiveProps(props){
            console.log('B---componentWillReceiveProps',props);
        }

        // 控制组件更新的“阀门”
        shouldComponentUpdate(){
            console.log('B---shouldComponentUpdate');
            return true;
        }

        // 组件将要更新的钩子
        componentWillUpdate(){
            console.log('B---componentWillUpdate');
        }

        // 组件更新完毕的钩子
        componentDidUpdate(){
            console.log('B---componentDidUpdate');
        }
        render(){
            console.log('B---render');
            return(
                <div>我是B组件,接收到的车是:{this.props.carName}</div>
            )
        }
    }

B组件中的输出

6.总结生命周期(旧)

  1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
    1.constructor()
    2.componentWillMount()
    3.render()
    4.componentDidMount() === > 常用。一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  2. 更新阶段: 由组件内部this.setSate()或父组件render触发
    1.shouldComponentUpdate()
    2.componentWillUpdate()
    3.render() === > 必须使用的
    4.componentDidUpdate()

  3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
    1.componentWillUnmount()===> 常用。一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

7.对比新旧生命周期

之前的依赖包版本均为16.8.4

下载新版本的依赖包:React官网–>文档–>CDN链接–>打开你想下载的那个文件的src,ctrl+s保存即可

推荐网站:BootCDN,前端人员一些常用的js库的线上地址

新生命周期图👇

与旧生命周期相比:
即将废弃三个钩子(componentWillMount、componentWillUpdate、componentWillReceiveProps),新增两个钩子(getDerivedStateFromProps、getSnapshotBeforeUpdate)

8.getDerivedStateFromProps

了解即可
官网上的介绍👇
在这里插入图片描述

ReactDOM.render(<Add count={199}/>,document.getElementById('test'));
	static getDerivedStateFromProps(props,state){
            console.log('Add---getDerivedStateFromProps',props,state);
            return props
        }

在这里插入图片描述

9.getSnapshotBeforeUpdate

官网上的介绍👇

// 在更新之前获取快照
        getSnapshotBeforeUpdate(){
            console.log('Add---getSnapshotBeforeUpdate');
            return 'atguigu'
        }
// 组件更新完毕的钩子
        componentDidUpdate(preProps,preState,snapshotValue){
            console.log('Add---componentDidUpdate',preProps,preState,snapshotValue);
        }

10.getSnapshotBeforeUpdate举例

class NewsList extends React.Component{

        state = {newsArr:[]}

        componentDidMount(){
            setInterval(()=>{
                // 获取原状态
                const {newsArr}=this.state
                // 模拟一条新闻
                const news='新闻'+ (newsArr.length+1); 
                // 更新状态
                this.setState({newsArr:[news,...newsArr]});
            },1000);
        }

        getSnapshotBeforeUpdate(){
            // 内容区现在的高度
            return this.refs.list.scrollHeight;
        }

        componentDidUpdate(preProps,preState,height){
            //  新的高度减去上一步传来的高度
            this.refs.list.scrollTop+=this.refs.list.scrollHeight-height;
        }

        render(){
            return (
                <div className="list" ref="list">
                    {
                        this.state.newsArr.map((n,index)=>{
                            return <div key={index} className="news">{n}</div>
                        })
                    }
                </div>
            )
        }
    }

11.总结生命周期(新)

    1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
                    1.constructor()
                    2.getDerivedStateFromProps 
                    3.render()
                    4.componentDidMount() ===> 常用。一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
	2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
                    1.getDerivedStateFromProps
                    2.shouldComponentUpdate()
                    3.render()
                    4.getSnapshotBeforeUpdate
                    5.componentDidUpdate()
	3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
                    1.componentWillUnmount() ===> 常用。一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

五、DOM的diffing算法

1.验证diffing算法

class Time extends React.Component{
        state={time:new Date()}
        componentDidMount(){
            setInterval(()=>{
                this.setState({
                    time:new Date()
                })
            },1000)
        }

        render(){
            // input框中的输入值不会消失,diffing算法:最小粒度是标签(节点)
            return (
                <div>
                    <h2>hello diffing</h2>    
                    <input type="text"/>
                    <span>现在是:{this.state.time.toTimeString()}
                    <input type="text"/>    
                    </span>
                </div>
            )
        }
    }

input框中的值不会发生变化:

2.key的作用

class Person extends React.Component{
        state={persons:[
            {id:1,name:'小李',age:18},
            {id:2,name:'小王',age:19},
        ]
    }

    add=()=>{
        const {persons}=this.state;
        const p={id:persons.length+1,name:'小赵',age:20};
        this.setState({persons:[p,...persons]});
    }
        render(){
            return (
                <div>
                    <h2>展示人员信息</h2>  
                    <button onClick={this.add}>添加一个小赵</button>  
                    <h3>使用index(索引值)作为key</h3>
                    <ul>
                        {
                            this.state.persons.map((personObj,index)=>{
                                return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li>
                            })
                        }
                    </ul>
                    <hr/>
                    <hr/>
                    <h3>使用id(数据的唯一标识)作为key</h3>
                    <ul>
                        {
                            this.state.persons.map((personObj)=>{
                                return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li>
                            })
                        }
                    </ul>
                </div>
            )
        }
    }

  1. 虚拟DOM中key的作用:
    当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
    a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
    (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
    (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
    根据数据创建新的真实DOM,随后渲染到到页面
  2. 用index作为key可能会引发的问题:
    (1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 = => 界面效果没问题, 但效率低。
    (2)如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
    (3)注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
  3. 开发中如何选择key?
    (1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
    (2)如果确定只是简单的展示数据,用index也是可以的。

写在后面

一周了,从上周日到现在,看完了三分之一的视频。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值