组件实例的三大核心属性

组件实例的三大核心属性

1、state

1.1 理解
  1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  2. 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
1.2 使用
  1. 组件中render方法中的this为组件实例对象
  2. 组件自定义的方法中this为undefined,如何解决?
    a) 强制绑定this: 通过函数对象的bind()
    b) 箭头函数
  3. 状态数据,不能直接修改或更新
    使用bind实现
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>state</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
 
    <script type="text/babel">
        //1.创建组件
        class Weather extends React.Component{
            /*
                总结:
                    1.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
                    2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
                    3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。
            */
 
            //构造器调用几次? ———— 1次
            constructor(props){
                console.log('constructor');
                // 如果A类继承了B类,且A类中写了构造器constructor,那么A类构造器中的super()是必须要调用的。
                 // 继承的子类没有this必须调用super
                super(props)
                //初始化状态
                this.state = {isHot:false,wind:'微风'}
                //解决changeWeather中this指向问题
                this.changeWeather = this.changeWeather.bind(this)
            }
 
            //render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数 只要调用了setState就会触发render
            render(){
                console.log('render');
                //读取状态
                const {isHot,wind} = this.state
                return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}{wind}</h1>
            }
 
            //changeWeather调用几次? ———— 点几次调几次
            changeWeather(){
                // changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
                // 只有通过Weather实例对象去调用changeWeather的时候,changeWeather中的this才是Weather的实例对象
                // 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
                // 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
 
                console.log('changeWeather');
                //获取原来的isHot值
                const isHot = this.state.isHot
                //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
                this.setState({isHot:!isHot})
                console.log(this);
 
                //严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
                //this.state.isHot = !isHot //这是错误的写法
            }
        }
        //2.渲染组件到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))
 
    </script>
</body>
</html>

使用箭头函数精简实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>state简写方式</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
 
    <script type="text/babel">
        //1.创建组件
        class Weather extends React.Component{
            //初始化状态
             //类中可以直接写赋值语句,如下代码的含义是:给Weather的实例对象上添加一个属性,名为state,值为一个对象
            state = {isHot:false,wind:'微风'}
 
            render(){
                const {isHot,wind} = this.state
                return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}{wind}</h1>
            }
 
            //自定义方法————要用赋值语句的形式+箭头函数
            // 箭头函数没有自己的this,它只会从自己的作用域链的上一层继承this。不同于在普通函数里,this的值在定义时是不明确的,要等到调用时才能确定,谁调用就指向谁。在箭头函数中,this是在创建时就已经确定了。
            changeWeather = ()=>{
                const isHot = this.state.isHot
                this.setState({isHot:!isHot})
            }
        }
        //2.渲染组件到页面
        ReactDOM.render(<Weather/>,document.getElementById('test'))
 
    </script>
</body>
</html>

2、props

在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它

2.1 理解
  1. 每个组件对象都会有props(properties的简写)属性
  2. 组件标签的所有属性都保存在props中
2.2 作用
  1. 通过标签属性从组件外向组件内传递变化的数据
  2. 注意: 组件内部不要修改props数据
2.3 使用
2.3.1 基本使用

注意:

  • 内部读取某个属性值,使用this.props.propsName。
  • 使用扩展运算符(...)批量传递属性。
  • 使用{}可以传数值类型,列如:age={19}, 直接写age=19是错误的key-value。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>props基本使用</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test1"></div>
    <div id="test2"></div>
    <div id="test3"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
 
    <script type="text/babel">
        //创建组件
        class Person extends React.Component{
            render(){
                // render里的this就是组件的实例对象
                // console.log(this);
                const {name,age,sex} = this.props
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test1'))
        ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
 
        const p = {name:'老刘',age:18,sex:'女'}
        // console.log('@',...p);
        // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
        ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
    </script>
</body>
</html>
2.3.2 对props中的属性值进行类型限制和必要性限制

第一种方式(React v15.5 开始已弃用):从React身上取PropTypes来做标签限制

Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}

第二种方式(新):使用prop-types库进限制(需要引入prop-types库)

  • propTypes:对标签属性进行类型、必要性的限制
  • defaultProps:指定默认标签属性值
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对props进行限制</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test1"></div>
    <div id="test2"></div>
    <div id="test3"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>
 
    <script type="text/babel">
        //创建组件
        class Person extends React.Component{
            render(){
                // console.log(this);
                const {name,age,sex} = this.props
                //props是只读的
                //this.props.name = 'jack' //此行代码会报错,因为props是只读的
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }
        //对标签属性进行类型、必要性的限制
        Person.propTypes = {
            name:PropTypes.string.isRequired, //限制name必传,且为字符串
            sex:PropTypes.string,//限制sex为字符串
            age:PropTypes.number,//限制age为数值
            speak:PropTypes.func,//限制speak为函数
        }
        //指定默认标签属性值
        Person.defaultProps = {
            sex:'男',//sex默认值为男
            age:18 //age默认值为18
        }
        //渲染组件到页面
        ReactDOM.render(<Person name={100} speak={speak}/>,document.getElementById('test1'))
        ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
 
        const p = {name:'老刘',age:18,sex:'女'}
        // console.log('@',...p);
        // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
        ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
 
        function speak(){
            console.log('我说话了');
        }
    </script>
</body>
</html>

props的简写方式:使用static关键字来给类的自身加属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对props进行限制</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test1"></div>
    <div id="test2"></div>
    <div id="test3"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>
 
    <script type="text/babel">
        //创建组件
        class Person extends React.Component{
 
            constructor(props){
                //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
                // console.log(props);
                super(props)
                console.log('constructor',this.props);
            }
 
            //对标签属性进行类型、必要性的限制
            static propTypes = {
                name:PropTypes.string.isRequired, //限制name必传,且为字符串
                sex:PropTypes.string,//限制sex为字符串
                age:PropTypes.number,//限制age为数值
            }
 
            //指定默认标签属性值
            static defaultProps = {
                sex:'男',//sex默认值为男
                age:18 //age默认值为18
            }
 
            render(){
                // console.log(this);
                const {name,age,sex} = this.props
                //props是只读的
                //this.props.name = 'jack' //此行代码会报错,因为props是只读的
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                )
            }
        }
 
        //渲染组件到页面
        ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
    </script>
</body>
</html>

3、refs与事件处理

3.1 理解

组件内的标签可以定义ref属性来标识自己,不能在函数组件上使用 ref 属性,因为他们没有实例。

3.2 何时使用
  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

注意:避免使用 refs 来做任何可以通过声明式实现来完成的事情。

3.3 使用
3.3.1 字符串形式的ref

注: refsName为标签中设置的ref的值
string类型的refs,我们可以通过 this.refs.refsName 来访问 DOM 节点,这种方法最简单粗暴,但是因为存在一定的效率问题,所以React官方文档中说不建议使用它,因为 string 类型的 refs 存在 一些问题它已过时并可能会在未来的版本被移除,官方建议用回调函数或 createRef API 的方式代替

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1_字符串形式的ref</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
 
    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            //展示左侧输入框的数据
            showData = ()=>{
                const {input1} = this.refs
                alert(input1.value)
            }
            //展示右侧输入框的数据
            showData2 = ()=>{
                const {input2} = this.refs
                alert(input2.value)
            }
            render(){
                return(
                    <div>
                        <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
    </script>
</body>
</html>
3.3.2 回调形式的refs

回调refs会传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。

关于回调refs执行次数的说明:如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null(注:由于不确定之前的函数传了什么值做了什么操作,所以为了保证函数中的操作被完全清空,所以传了个null来做清空),然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

更新的时候,我们需要更改状态,react中,状态驱动着页面显示,即当我们更改了状态之后就会重新调用render,然后代码执行到render中时,发现节点上有ref,并且是函数式ref,这个函数已经不是之前的那个函数了,旧函数在执行完后就被释放了,所以他会去执行函数,但由于不确定之前的函数传了什么值做了什么操作,所以为了保证函数中的操作被完全清空,所以传了个null来做清空,第二次再传入真正的节点。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>3_回调ref中回调执行次数的问题</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
 
    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
 
            state = {isHot:false}
 
            showInfo = ()=>{
                const {input1} = this
                alert(input1.value)
            }
 
            changeWeather = ()=>{
                //获取原来的状态
                const {isHot} = this.state
                //更新状态
                this.setState({isHot:!isHot})
            }
 
             //回调形式的ref,方法放在类的实例自身上,重新调用时,它已经知道该方法已存在,之前调过,不是一个新函数
            saveInput = (c)=>{
                this.input1 = c;
                console.log('@',c);
            }
 
            render(){
                const {isHot} = this.state
                return(
                    <div>
                        <h2>今天天气很{isHot ? '炎热':'凉爽'}</h2>
                        {/*内联写法如下*/}
                        {/*<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/}
                        {/*定义成class的绑定函数写法如下*/}
                        <input ref={this.saveInput} type="text"/><br/><br/>
                        <button onClick={this.showInfo}>点我提示输入的数据</button>
                        <button onClick={this.changeWeather}>点我切换天气</button>
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>
</body>
</html>
3.3.3 使用createRef创建ref容器 — React最推荐的一种形式,React 16.3 版本引入

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的

  • ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。
  • ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。
  • 你不能在函数组件上使用 ref 属性,因为他们没有实例。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4_createRef</title>
</head>
<body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
 
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
 
    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            /*
                React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
             */
            myRef = React.createRef()
            myRef2 = React.createRef()
            //展示左侧输入框的数据
            showData = ()=>{
                alert(this.myRef.current.value);
            }
            //展示右侧输入框的数据
            showData2 = ()=>{
                alert(this.myRef2.current.value);
            }
            render(){
                return(
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                        <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
    </script>
</body>
</html>
3.4 总结
  • React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMountcomponentDidUpdate 生命周期钩子触发前更新。
  • 默认情况下,不能在函数组件上使用 ref 属性但是可以在函数组件内部使用 ref 属性,只要它指向一个 DOM 元素或 class 组件。如果想在函数组件中使用 ref,在16.3 或更高版本中React推荐开发者使用ref 转发,使用 16.2 或更低版本的 React,或者你需要比 ref 转发更高的灵活性,可以使用这个替代方案将 ref 作为特殊名字的 prop 直接传递。
  • 不要过度使用ref,ref从一定程度上增加了组件之间的耦合性,导致难以分离,所以如果可以用props来处理的事情,尽量不要用ref来处理,还可以通过event.target得到发生事件的DOM元素对象。

4、事件处理

  1. 通过onXxx属性指定事件处理函数(注意大小写)

    1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性

    2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效

  2. 通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值