React——组件的三大核心属性【七】

前言

组件的三大核心属性

内容

state

定义一个展示天气信息组件,通过点击切换天气信息

理解

  1. state是组件对象最重要的属性,值是对象(可以包含多key-value组合)
  2. 组件被称为状态机,通过更新组件的state来重新渲染组件
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以text/babel来声明*/
//1. 创建组件

class Weather extends React.Component {

    //调用1次
    constructor(props) {
        super(props);
        //初始化状态
        this.state = { isHot: true };
        //解决changeWeather的指向问题
        this.changeWeather = this.changeWeather.bind(this)
    }
    //调用次数1+n 1是初始化,n是状态更新
    render() {
        console.log(this)
        //读取状态
        const {isHot} = this.state
        return (
            <div>
               <h1 onClick={this.changeWeather}>今天天气 {isHot ? '热热' : '不热热'}</h1>
            </div>
        );
    }
    //点击几次调用几次
    changeWeather(){
        //状态不可直接更改需要借助内置方法(setState)进行更改
        const isHot = this.state.isHot
        this.setState({isHot:!isHot})
    }
}

//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

</script>
</body>
</html>

简写

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以text/babel来声明*/
//1. 创建组件

class Weather extends React.Component {
    
    render() {
        const {isHot} = this.state
        return (
            <div>
               <h1 onClick={this.changeWeather}>今天天气 {isHot ? '热热' : '不热热'}</h1>
            </div>
        );
    }
    //赋值语句+箭头函数
    changeWeather = () => {
        const isHot = this.state.isHot
        this.setState({isHot:!isHot})
    }
}

//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

</script>
</body>
</html>

注意点

1. 组件中的render方法中的this为组件实例对象
2. 组件中自定义的方法中的为undefined,如何解决?
   2.1 前置绑定this:通过函数对象的bind()
   2.2 赋值语句+箭头函数
3. 状态数据,不能直接修改或更新,需通过setState来变更

props

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

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

理解

  1. 每个组件对象都会有props(properties)属性
  2. 组件标签的所有属性都保存在props中
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<!--创建"容器"-->
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></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">/*一定要以text/babel来声明*/
//1. 创建组件

class Person extends React.Component {
    render() {
        //props是只读的,不可进行修改
        const {name,age,sex} = this.props
        return (
            <div>
                <ul>
                    <li>姓名:{name}</li>
                    <li>年龄:{age}</li>
                    <li>性别:{sex}</li>
                </ul>
            </div>
        );
    }
}

//对标签属性进行类型,必要性限制
Person.protoType = {
    name: PropTypes.string.isRequired, // 限制name必传且类型为string
    age: PropTypes.number,
    sex: PropTypes.string,
    speak: PropTypes.func
}

//指定默认标签属性值
Person.defaultProps = {
    sex: "男",
    age: 18
}

function speak() {
    return "哈哈哈哈"
}

//2.渲染组件到页面
const p = {name:"张三", age:66, sex:'男'}
ReactDOM.render(<Person name="tom" age={19} sex="男" speak={speak}/>,document.getElementById('test'))
ReactDOM.render(<Person name="jim" age="10" sex="男"/>,document.getElementById('test1'))
//语法糖
ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))

</script>
</body>
</html>

简写

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<!--创建"容器"-->
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></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">/*一定要以text/babel来声明*/
//1. 创建组件

class Person extends React.Component {
    // constructor(props) {
    //     //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
    //     super(props);
    // }
    //对标签属性进行类型,必要性限制
    static protoType = {
        name: PropTypes.string.isRequired, // 限制name必传且类型为string
        age: PropTypes.number,
        sex: PropTypes.string
    }

    //指定默认标签属性值
    static defaultProps = {
        sex: "男",
        age: 18
    }

    render() {
        //props是只读的,不可进行修改
        const {name,age,sex} = this.props
        return (
            <div>
                <ul>
                    <li>姓名:{name}</li>
                    <li>年龄:{age}</li>
                    <li>性别:{sex}</li>
                </ul>
            </div>
        );
    }
}


ReactDOM.render(<Person name="tom" />,document.getElementById('test'))

</script>
</body>
</html>

函数式组件使用props

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<!--创建"容器"-->
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></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">/*一定要以text/babel来声明*/
//1. 创建组件
function Person(props) {
    const {name, age, sex} = props
    return (
        <div>
            <ul>
                <li>姓名:{name}</li>
                <li>年龄:{age}</li>
                <li>性别:{sex}</li>
            </ul>
        </div>
    )  ;
}

    //对标签属性进行类型,必要性限制
     Person.protoType = {
        name: PropTypes.string.isRequired, // 限制name必传且类型为string
        age: PropTypes.number,
        sex: PropTypes.string
    }

    //指定默认标签属性值
     Person.defaultProps = {
        sex: "男",
        age: 18
    }

ReactDOM.render(<Person name="tom" sex="女" age={18}/>,document.getElementById('test'))

</script>
</body>
</html>

注意点

1. 通过标签属性从组件外向组件内传递变化的数据
2. 注意: 组件内部不要修改props数据
3. 内部通过this.props.xx读取某个属性值
4. props中的属性值进行类型限制和必要性限制
   4.1 React v15.5 开始已弃用
        Person.propTypes = {
         name: React.PropTypes.string.isRequired,
         age: React.PropTypes.number
        }
   4.2 使用prop-types库进限制(需要引入prop-types库)
       Person.propTypes = {
        name: PropTypes.string.isRequired,
        age: PropTypes.number. 
       }
5. 扩展属性: 将对象的所有属性通过props传递
   <Person {...person}/>
6. 默认属性值
   Person.defaultProps = {
    age: 18,
    sex:'男'
   }

ref

  1. 点击按钮, 提示第一个输入框中的值
  2. 当第2个输入框失去焦点时, 提示这个输入框中的值

理解

组件内的标签可以定义ref属性来标识自己

字符串类型ref

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以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>
                <br/>
                <hr/>
                <input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点提示数据"/>
            </div>
        );
    }
}

ReactDOM.render(<Demo/>, document.getElementById("test"))


/**
 * string类型的ref是过时的API
 * https://react.docschina.org/docs/refs-and-the-dom.html#legacy-api-string-refs
 *
 * https://github.com/facebook/react/pull/8333#issuecomment-271648615
 */

</script>
</body>
</html>

回调函数类型ref

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以text/babel来声明*/
class Demo extends React.Component {

    //左侧
    showData = () => {
        const {input1} = this
        alert(input1.value)
    }

    //右侧
    showData2 = () => {
        const {input2} = this
        alert(input2.value)
    }
    render() {
        return (
            <div>
                <input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据"/> &nbsp;
                <button  onClick={this.showData}>点我提示左侧数据</button>
                <br/>
                <hr/>
                <input  ref={currentNode => this.input2 = currentNode} onBlur={this.showData2}  type="text" placeholder="失去焦点提示数据"/>
            </div>
        );
    }
}

ReactDOM.render(<Demo />, document.getElementById("test"))



</script>
</body>
</html>


如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以text/babel来声明*/
class Demo extends React.Component {

    state = {isHot:true}

    changeWeather = () => {
        const {isHot} = this.state
        this.setState({isHot:!isHot})
    }

    //左侧
    showData = () => {
        const {input1} = this
        alert(input1.value)
    }

    saveInput = (c) => {
        this.input1 = c;
        console.log('@',c);
    }

    render() {
        const {isHot} = this.state
        return (
            <div>
                <h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
                <button onClick={this.changeWeather}>点击切换天气</button>
                {/*<input ref={(currentNode) => { this.input1 = currentNode; console.log('@',currentNode);} }  type="text" placeholder="点击按钮提示数据"/> &nbsp;*/}
                <input ref={this.saveInput}  type="text" placeholder="点击按钮提示数据"/> &nbsp;
                <button  onClick={this.showData}>点我提示左侧数据</button>
            </div>
        );
    }
}

ReactDOM.render(<Demo />, document.getElementById("test"))

</script>
</body>
</html>


createRef

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以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>
                <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="=失去焦点显示数据"/> &nbsp;
            </div>
        );
    }
}

ReactDOM.render(<Demo />, document.getElementById("test"))



</script>
</body>
</html>


注意点

1.  string类型的ref是过时的API,可能会在未来版本被移除,建议使用回调函数或createRef API来代替。
    <input ref="input1"/>

2. 回调函数类型的ref
   <input ref={(c)=>{this.input1 = c}} />
   2.1 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素
       <input ref={this.saveInput} /> //通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题

3. createRef创建ref容器
   myRef = React.createRef() 
   <input ref={this.myRef}/>
 

事件处理

1.通过onXxx属性指定事件处理函数(注意大小写)
  1) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件___兼容性
  2) React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)___高效性
2.通过event.target得到发生事件的DOM元素对象___不要过度使用ref
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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">/*一定要以text/babel来声明*/
class Demo extends React.Component {

    /**
     1.	通过onXxx属性指定事件处理函数(注意大小写)
         1)	React使用的是自定义(合成)事件, 而不是使用的原生DOM事件___兼容性
         2)	React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)___高效性
     2.	通过event.target得到发生事件的DOM元素对象___不要过度使用ref
     *
     */

    // 创建ref容器
    myRef = React.createRef()
    myRef2 = React.createRef()


    //左侧
    showData = () => {
        alert(this.myRef.current.value)
    }

    //右侧
    showData2 = (event) => {
        alert(event.target.value)
    }
    render() {
        return (
            <div>
                <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/> &nbsp;
                <button  onClick={this.showData}>点我提示左侧数据</button>
                <input onBlur={this.showData2} type="text" placeholder="=失去焦点显示数据"/> &nbsp;
            </div>
        );
    }
}

ReactDOM.render(<Demo />, document.getElementById("test"))



</script>
</body>
</html>


posted @ 2021-02-25 14:53  。思索  阅读(254)  评论(0编辑  收藏  举报