React类组件函数组件&& 组件实例的三大核心属性

41 阅读4分钟

React类组件函数组件&& 组件实例的三大核心属性

目录

[TOC]

基本理解和使用

React面向组件编程
函数式组件:

此处this是undefinedbabel编译后开启了严格模式

执行了 ReactDOM.render(, document.getElementById('test'))之后

  • React解析组件标签,找到Demo组件
  • 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM呈现在页面中
<!-- 引入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>
<!-- 一定要写babel -->
<script type="text/babel">
    //创建函数式组件
    function Demo() {
        console.log(this);//undefined 
        return <h2>我是一个函数定义的组件(适用于[简单组件]的定义)</h2>
    }
    //渲染组件到组件
    ReactDOM.render(<Demo />, document.getElementById('test'))
</script>

注意

  1. 组件名必须首字母大写标签
  2. 虚拟DOM元素只能有一个根元素
  3. 虚拟DOM元素必须有结束标签
  4. 函数必须有返回值
复习类的相关知识
  • 类中的构造器,不是必须写的,要对实例进行一些初始化的操作 如添加指定属性时才写
  • 如果子类继承了父类,且子类写了构造器,那么子类构造器中super是必须要定义的
  • 类中所定义的方法,都放在了类的原型对象上供实例使用
  • a=1 类中可以直接写赋值语句 给实例对象添加一个属性名为a 值为1
  <script>
        //创建一个parson类
        class Parson {
            constructor(name, age) {
                //构造器中的this是实例对象
                this.name = name
                this.age = age
            }
            a=1//类中可以直接写赋值语句 给实例对象添加一个属性名为a 值为1
 
            // 一般方法
            speak() {
                // spak 放在了类的原型对象上,供实例使用的
                // 通过parson实例调用speak时,speak中的this就是parson实例
                console.log(`我叫${this.name}我的年龄是${this.age}`);
            }
        }
        //  创建一个Student类,继承于Parson类
        class Student extends Parson {
            constructor(name, age, grade) {
                //继承属性必须写super
                super(name, age)
                this.grade = grade
            }
            speak() {
                console.log(`我叫${this.name}我的年龄是${this.age},我的年级是${this.grade}`);
            }
            study() {
                // study 放在了类的原型对象上,供实例使用的
                // 通过Student实例调用study时,study中的this就是Student实例
                console.log('我很努力的学习');
            }
        }
 
    </script>
类式组件:

必须继承React.Component,要有返回值

必须写render    rouder放在类的原型对象上,供实例实用  this是 组件的实例对象

执行了 ReactDOM.render(, document.getElementById('test'))之后

  • React解析组件标签,找到Demo组件
  • 发现组件是使用类定义的,随后new出该类的实例,并通过该实例调用到原型上的render方法
  • 将render返回的虚拟DOM转为真实DOM随后呈现在页面中
<script type="text/babel">
    //创建类组件
    class Demo extends React.Component {
        //   rouder放在类的原型对象上,供实例实用
        //   this是 Demo 的实例对象
        render() {
            return <h2>我是一个类定义的组件(适用于[复杂组件]的定义)</h2>
        }
    }
    ReactDOM.render(<Demo />, document.getElementById('test'))

组件实例的三大核心属性

类中方法的this指向

通过实例调用this为实例

把方法赋值给x变量相当于把方法赋值给了x变量 这时this发生变化(类中的方法开启局部严格模式)所以为undefined

bind可以生成一个新函数改变this指向

      //创建一个parson类
        class Parson {
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            speak() {
                // spak 放在了类的原型对象上,供实例使用的
                // 通过parson实例调用speak时,speak中的this就是parson实例
                console.log(this);
            }
        }
        const p1 = new Parson()
        p1.speak()//通过实例调用speak方法
        const x = p1.speak
        x() //undefined
state
  1. state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  2. 组件被称为"状态机",通过更新组件的state来更新对应的页面显示(重新渲染组件)

强烈注意

  1. 组件中render方法中的this为组件实例对象

  2. 组件自定义的方法中thisundefined,如何解决?

    1. 强制绑定this:通过函数对象的bind()
    2. 箭头函数
  3. 状态数据,不能直接修改或更新,要使用React.Component父类提供的setState方法

  4. 构造器执行一次,renderd调用1+n次 1是初始化 n是状态更新的次数  方法点几次调用几次

需求 : 定义一个展示天气信息的组件 默认展示天气炎热 或 凉爽 点击文字切换天气

完整版

<script type="text/babel">
    class Weather extends React.Component {
        //构造器执行一次
        constructor(props) {
            super(props)
            this.state = { isHot: true }
            //解决change中的this指向问题(把右侧类中的change修改后的新函数赋值给change)
            this.change = this.change.bind(this)//this为实例
        }
 
        // renderd调用1+n次 1是初始化 n是状态更新的次数
        render() {
            // 读取状态
            console.log(this);//组件的实例对象
            const { isHot } = this.state
            return <h2 onClick={this.change} > 天气很{isHot ? '炎热' : '凉爽'}</h2 >
        }
 
        //点几次调用几次
        change() {
            //change放在 Weather 的实例对象上
            //由于 change 是作为onClick的回调所以不是通过实例调用的,是直接调用
            //类中的方法默认开启局部严格模式所以change中的this为 undefined
 
            //获取原来的 isHot 值
            const isHot = this.state.isHot
            // 状态数据,不能直接修改或更新,要使用 React.Component 父类提供的setState方法.且更新是一种合并,不是替换
 
            this.setState({ isHot: !isHot })
 
 
            // //状态不可以直接更改
            // this.state.isHot = !isHot
 
        }
    }
    ReactDOM.render(<Weather />, document.getElementById('test'))

简化版

<!-- 一定要写babel -->
<script type="text/babel">
    class Weather extends React.Component {
        //初始化状态 给实例对象添加
        state = { isHot: true }
        render() {
            const { isHot } = this.state
            return <h2 onClick={this.change} > 天气很{isHot ? '炎热' : '凉爽'}</h2 >
        }
        //自定义方法--->赋值语句+箭头函数,放在实例身上
        change = () => {
            const isHot = this.state.isHot
            this.setState({ isHot: !isHot })
        }
    }
    ReactDOM.render(<Weather />, document.getElementById('test'))
 
</script>
props
  1. 每个组件对象都会有props(properties的简写)属性
  2. 组件标签的所有属性都保存在props中
  3. 通过标签属性从组件外向组件内传递变化的数据
  4. 注意:组件内部不要修改props数据
展开运算符
  • [...arr1, ...arr2] 连接数组
  • let obj2 = { ...obj } 构建字面量对象时使用展开运算符,展开运算符不能展开对象
  • 合并  let obj3 = { ...obj, name: 'lisa', address: '地球' }
  • reduce  依次迭代数组每一项,他会把上一次迭代中,回调函数处理的结果传递给下一次迭代,以此实现结果的累积 a之前的值,b当前的值 有返回值
    <script>
        let arr1 = [1, 3, 5, 7, 9]
        let arr2 = [2, 4, 6, 8, 10]
        console.log(...arr1);//展开一个数组
 
        let arr3 = [...arr1, ...arr2]
 
        console.log(arr3);//连接数组
 
        //在函数中使用
        function sum(...numbers) {
            // 依次迭代数组每一项,他会把上一次迭代中,回调函数处理的结果传递给下一次迭代,以此实现结果的累积 a之前的值,b当前的值 有返回值
            return numbers.reduce((a, b) => {
                console.log(a, b);
                return a + b
            })
        }
        console.log(sum(sum(1, 2, 3, 4)));
 
        //构建字面量对象时使用展开运算符
        let obj = { name: 'tom', age: 18 }
        // console.log(...obj);//展开运算符不能展开对象
 
        let obj2 = { ...obj }
        console.log(obj2);//克隆一个对象
 
        //合并
        let obj3 = { ...obj, name: 'lisa', address: '地球' }
        console.log(obj3);
    </script>

编码操作

  1. 内部读取某个属性值this.props.name
  2. props中的属性值进行类型限制和必要性限制

3.扩展属性:将对象的所有属性通过props传递<Person{...p} />

4.默认属性值:

5.组件类的构造函数

效果

需求 : 自定义用来显示一个人员信息的组件

  1. 姓名必须指定,且为字符串类型;
  2. 性别为字符串类型,如果性别没有指定,默认为男
  3. 年龄为字符串类型,且为数字类型,默认值为1 8

完整版

<!-- 引入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>
<!-- 一定要写babel -->
<script type="text/babel">
    class Person extends React.Component {
 
        render() {
            console.log(this);
            const { name, age, sex } = this.props
            // this.props.name = 'ls' 此处会报错,因为props是只读的
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{age}</li>
                    <li>年龄:{sex}</li>
                </ul>
            )
        }
 
    }
    const p = { name: 'tom', age: 18, sex: '女' }
    //对标签属性进行类型,必要性的限制
    Person.propTypes = {
        name: PropTypes.string.isRequired,//限制name 必传,且为字符串
        age: PropTypes.number,
        sex: PropTypes.string,
        speak: PropTypes.func,//限制 speak 为函数
    }
 
    //指定默认标签属性值
    Person.defaultProps = {
        sex: '女',//sex默认值为女
        age: 18
    }
 
    // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex} />, document.getElementById('test'))
    ReactDOM.render(<Person{...p} speak={speak} />, document.getElementById('test'))
    function speak() {
        console.log('我说话了');
    }

简写

  • status={}直接在类里写相当于给实例对象添加属性方法
  • static 相当于给类加属性和方法
<script type="text/babel">
    class Person extends React.Component {
        render() {
            console.log(this);
            const { name, age, sex } = this.props
            // this.props.name = 'ls' 此处会报错,因为props是只读的
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{age}</li>
                    <li>年龄:{sex}</li>
                </ul>
            )
        }
        //对标签属性进行类型,必要性的限制
        static propTypes = {
            name: PropTypes.string.isRequired,//限制name 必传,且为字符串
            age: PropTypes.number,
            sex: PropTypes.string,
            speak: PropTypes.func,//限制 speak 为函数
        }
 
        //指定默认标签属性值
        static defaultProps = {
            sex: '女',//sex默认值为女
            age: 18
        }
    }
    const p = { name: 'tom', age: 18, sex: '女' }
    ReactDOM.render(<Person{...p} speak={speak} />, document.getElementById('test'))
    function speak() {
        console.log('我说话了');
    }
类中的构造器

//构造器中是否接收props 是否传递给super 取决于是否希望在构造器中通过this访问props

  class Person extends React.Component {
        constructor(props) {
            //构造器中是否接收props 是否传递给super 取决于是否希望在构造器中通过this访问props
            super(props)
            console.log(this.props);
        }
 
        //对标签属性进行类型,必要性的限制
        static propTypes = {
            name: PropTypes.string.isRequired,//限制name 必传,且为字符串
            age: PropTypes.number,
            sex: PropTypes.string,
 
        }
 
        //指定默认标签属性值
        static defaultProps = {
            sex: '女',//sex默认值为女
            age: 18
        }
        render() {
            console.log(this);
            const { name, age, sex } = this.props
            // this.props.name = 'ls' 此处会报错,因为props是只读的
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{age}</li>
                    <li>年龄:{sex}</li>
                </ul>
            )
        }
 
    }
 
    ReactDOM.render(<Person name="lisa" />, document.getElementById('test'))
函数式组件使用props

可以通过形参接收props

限制标签属性要写在外面,函数组件没有static

<script type="text/babel">
 
    function Person(props) {
        const { name, age, sex } = props
        return (
            <ul>
                <li>姓名:{name}</li>
                <li>性别:{age}</li>
                <li>年龄:{sex}</li>
            </ul>
        )
    }
    Person.propTypes = {
        name: PropTypes.string.isRequired,//限制name 必传,且为字符串
        age: PropTypes.number,
        sex: PropTypes.string,
        speak: PropTypes.func,//限制 speak 为函数
    }
 
    //指定默认标签属性值
    Person.defaultProps = {
        sex: '女',//sex默认值为女
        age: 18
    }
    ReactDOM.render(<Person name="lisa" />, document.getElementById('test'))
 
</script>
符串形式的ref
  • 组件内的标签可以定义ref属性来标识自己
  • refs获取真实dom元素
  • 字符串的ref存在效率问题不推荐使用
 //创建类组件
    class Demo extends React.Component {
 
        render() {
            return (
                <div>
                    <input ref="input1" type="text" placeholder="点我提示数据" />
                    <button onClick={this.showData}>点我提示左侧的数据</button>
                    <input ref="input2" type="text" onBlur={this.showData2} placeholder="失去焦点提示数据" />
                </div>
            )
        }
        //展示左侧输入框数据
        showData = () => {
            console.log(this.refs);
            const { input1 } = this.refs
            alert(input1.value)
        }
        //展示右侧输入框数据
        showData2 = () => {
            console.log(this.refs);
            const { input2 } = this.refs
            alert(input2.value)
 
        }
 
    }
    ReactDOM.render(<Demo />, document.getElementById('test'))
回调形式的ref
  • 回调 自己定义的,自己没调用 函数最终执行了
  • 把ref当前所在节点挂在实例自身上  const { input2 } = this
  • 如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null ,然后第二次会传入参数 DOM 元素,这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的
  • 过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,
 <div>
<input ref={c => this.input1 = c} type="text" placeholder="点我提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={c => this.input2 = c} type="text" placeholder="点我提示数据" />
 <input onBlur={this.showData2} ref={this.saveInput} type="text" placeholder="点我提示数据" />
 
 </div>
saveInput = (c) => {
 this.input = c
 console.log('@', c);
 }
createRef创建ref容器·
  • //React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点
  • //该容器是专人专业的
  class Demo extends React.Component {
        render() {
            return (
                <div>
                    <input ref={this.myRef} type="text" placeholder="点我提示数据" />
                    <button onClick={this.showData}>点我提示左侧的数据</button>
 
                </div>
            )
        }
        //React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点
        //该容器是专人专业的
        myRef = React.createRef()
        //展示左侧输入框数据
        showData = () => {
            console.log(this.myRef.current.value);
 
        }
    }
    ReactDOM.render(<Demo />, document.getElementById('test'))