React 学习笔记2 props、refs

props

基础用法

在使用组件时,有时希望组件中展示组件定义外部的数据。

在组件上有一个props属性,在通过类式方法定义组件时,可以在渲染组件时,在组件标签上添加属性,这个属性会以key value的形式放入props。

html 复制代码
<body>
    <div id="demo">

    </div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

    <!-- Don't use this in production: -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script type="text/babel">
        class Cat extends React.Component {
            
            render() {
                return <h2>{this.props.name}--{this.props.color}</h2>
            }
            
        }


        ReactDOM.render(<Cat name="cat" color="orange"/>, document.getElementById('demo'))
    </script>
</body>

props批量传递

当这种传参方式也存在问题,一个问题是,当需要传入的参数过多时,很难每个都手写。一个解决办法是使用...语法:

实际上,对于对象类型来说,...展开运算符是无法直接应用在对象上的,但如果使用{...对象}的形式是可以的,相当于是在构造字面量对象时使用展开语法,相当于克隆了对象,而且是深拷贝。通过{...对象,要修改的value对应的key:新value}来进行修改,会对这些同名key进行合并。

而在React的标签中,书写{...对象}时,外部的{}并不是字面量克隆,而是表示内部的语法是JS表达式,相当于...对象,这是React内部允许的特殊写法,通过React+Babel,支持使用...对对象进行展开。但这种语法并不是在任意位置都会生效,这种语法仅仅适用于标签参数的传递。

html 复制代码
    <script type="text/babel">
        class Cat extends React.Component {
            
            render() {
                return <h2>{this.props.name}--{this.props.color}</h2>
            }
            
        }

        const data = {name:'cat',color:"orange",age:10}
        ReactDOM.render(<Cat {...data}/>, document.getElementById('demo'))
    </script>

限制props

通过属性传递key value时,value默认是字符串,如果需要在标签中传递数字,需要通过key={数字}的形式来进行,但可能代码是很多人书写的,有时候其他人并不会考虑后续使用的各种情况,这会导致代码产生各种各样的问题。如果希望传递的属性是某个固定的数值,就需要对props添加限制。

React支持对props的 数据类型、数据默认值、数据是否必须传递 进行限制。

这要通过给组件添加propTypes属性来实现。

propTypes的value是一个对象,通过在对象里添加配置,React可以给props添加限制。

通过key:PropTypes.需要传递的类型(小写)的形式可以对props的类型进行限制。通过.isRequired可以设置属性为必填项。通过对类加上defaultProps可以为属性设置默认值,defaultProps的value为对象,对象内部以key:默认值的形式进行设置。通过标签也能传递函数,函数的数据类型是func。

propTypes是需要限制props时,应该给类式组件添加的属性,而PropTypes是支持设置限制的方法,两者并不相同。

PropTypes并不是React内置的方法,需要npm install prop-types及import PropTypes from 'prop-types'来引入PropTypes。

html 复制代码
npm install prop-types

import PropTypes from 'prop-types'

而对于直接在HTML文件中编写的React Demo,可以通过js的方式进行引入:

javascript 复制代码
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
html 复制代码
<body>
    <div id="demo">

    </div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>


    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
    <script type="text/babel">

        class Cat extends React.Component {

            render() {
                return <h2>{this.props.name}--{this.props.color}--{this.props.say()}</h2>
            }

        }

        Cat.propTypes = {
            name: PropTypes.string.isRequired,
            age: PropTypes.number,
            say: PropTypes.func
        }
        
        function say(){
            console.log(this.name)
        }

        const data = { name: 'cat', color: "orange", age: 10 }
        ReactDOM.render(<Cat {...data} say={say} />, document.getElementById('demo'))
    </script>
</body>

props是只读的,通过props传递的参数,在类内部是无法修改的。

props简写形式

组件类.propTypes和组件类.defaultProps可以不在类外面编写,可以直接在类内部定义:

javascript 复制代码
    <script type="text/babel">

        class Cat extends React.Component {
            static propTypes = {
                name: PropTypes.string.isRequired,
                age: PropTypes.number,
                say: PropTypes.func
            }
            static defaultProps = {
                color: 'black'
            }
            render() {
                return <h2>{this.props.name}--{this.props.color}--{this.props.say()}</h2>
            }

        }

        function say() {
            console.log(this.name)
        }

        const data = { name: 'cat', age: 10 }
        ReactDOM.render(<Cat {...data} say={say} />, document.getElementById('demo'))
    </script>

构造器中的props

在通过构造器对属性进行操作时,构造器会有一个参数props,且在构造器内部需要super(props)。

为什么需要在构造器中编写props相关的代码呢?这是为了防止this.props调用时出现属性未定义的BUG。

类中构造器其实也可以不写props,也不会报错,但此时通过this.props访问得到的是undefined。如果不需要访问this.props,也可以完全不写props和super(props)。

函数式组件的props

对于函数式组件,虽然函数式组件中没有this,也没有state(可以通过hooks间接实现)和refs,但是函数式组件中有Props。

使用函数式组件时,用法和类式类似,也是在标签中编写要传递的数据,在函数中,通过参数props获得传递的数据。

函数式组件也可以对props进行限制,语法和类式组件类似,只不过函数式组件对props的限制只能写在函数外面。

javascript 复制代码
    <script type="text/babel">

        function Cat(props) {
            
            return <h2>{props.name}--{props.color}--{props.say()}</h2>
            

        }

        Cat.propTypes = {
                name: PropTypes.string.isRequired,
                age: PropTypes.number,
                say: PropTypes.func
            }
        Cat.defaultProps = {
                color: 'black'
            }

        function say() {
            console.log(this.name)
        }

        const data = { name: 'cat', age: 10 }
        ReactDOM.render(<Cat {...data} say={say} />, document.getElementById('demo'))
    </script>

refs

ref的value有三种形式:

字符串形式

ref是React中提供的一个属性,给标签加入ref属性后,组件实例上的refs对象中会出现这个ref,refs中的key是标签中给ref设定的value,refs对应的value是ref属性所在的标签。设置好ref后,通过this.refs.给ref设定的value,可以获取ref所在的标签。

通过refs获取的是真实DOM节点。

这种方式不被React推荐,在之后的版本中可能会被移除。字符串类型的ref存在效率问题。

javascript 复制代码
    <script type="text/babel">

        class Cat extends React.Component {
            alertData=()=>{
                const nowDOM = this.refs.inputData;
                alert(nowDOM.value)
            }

            render(){
                return (
                    <div>
                        <input ref="inputData"/>
                        <button onClick={this.alertData}>click</button>
                    </div>
                )
            }
        }
       

        const data = { name: 'cat', age: 10 }
        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>

回调函数形式

ref的value,也可以是一个回调函数。由于函数是JS表达式,需要包裹在{}中,这个回调函数的参数是ref属性所处的DOM,在函数体中,一般把这个属性放在组件实例上。

javascript 复制代码
    <script type="text/babel">

        class Cat extends React.Component {
            alertData=()=>{
                const nowDOM = this.inputDOM
                alert(nowDOM.value)
            }

            render(){
                return (
                    <div>
                        <input ref={ (currentDOM)=>{this.inputDOM = currentDOM} }/>
                        <button onClick={this.alertData}>click</button>
                    </div>
                )
            }
        }
       
        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>

回调函数在什么时候被调用呢?实际上,在初始时,这个回调函数会被调用一次。除此之外,如果ref是以内联函数形式定义的,在更新时(初始化加载时不是更新,更新是初始化之后,数据改变引起的更新)回调函数会被执行两次,第一次参数是null,第二次参数才是ref所在的DOM元素。这是因为在每次渲染时会创建一个新的函数实例,React在更新时,会清空旧的ref并设置新的。

javascript 复制代码
    <script type="text/babel">

        class Cat extends React.Component {
            alertData=()=>{
                const nowDOM = this.inputDOM
                alert(nowDOM.value)
            }

            state={
                flag:true,
            }

            changeFlag=()=>{
                const flag = this.state.flag
                this.setState( {
                    flag : !flag
                })
            }

            render(){
                return (
                    <div>
                        <input ref={ (currentDOM)=>{this.inputDOM = currentDOM;console.log(currentDOM)} }/>
                        <button onClick={this.alertData}>click</button>
                        <button onClick={this.changeFlag}>change</button>
                        <h2>{this.state.flag ? 'cat':'dog'}</h2>
                    </div>
                )
            }
        }
        
        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>

但这种情况一般并不会影响ref的功能,仅仅是会多调用一次。如果还是想避免这种情况,可以不使用内联函数,而是使用类内部绑定的函数。

javascript 复制代码
    <script type="text/babel">

        class Cat extends React.Component {
            alertData = () => {
                const nowDOM = this.inputDOM
                alert(nowDOM.value)
            }

            state = {
                flag: true,
            }

            changeFlag = () => {
                const flag = this.state.flag
                this.setState({
                    flag: !flag
                })
            }

            getInputDOM = (currentDOM) => {
                this.inputDOM = currentDOM
                console.log(currentDOM)
            }


            render() {
                return (
                    <div>
                        <input ref={this.getInputDOM} />
                        <button onClick={this.alertData}>click</button>
                        <button onClick={this.changeFlag}>change</button>
                        <h2>{this.state.flag ? 'cat' : 'dog'}</h2>
                    </div>
                )
            }
        }

        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>

JSX的注释以{/* */}的形式进行。{}表示要写JS表达式,内部的/**/是JS注释。

React.createRef()

ref的value,也可以通过React.createRef()来设定。

React.createRef是一个函数,调用之后会返回一个容器,可以存储被ref所标识的DOM节点。

当ref的value是createRef容器时,会把ref所在的DOM节点存储在容器中。且是存储在容器的current属性中。current中只能存储一个DOM。如果多个ref存储在一个容器中,后触发的存储会覆盖之前的。如果需要获取多个DOM,只能创建多个容器。

javascript 复制代码
    <script type="text/babel">

        class Cat extends React.Component {
            alertData = () => {
                const nowDOM = this.inputRef.current
                alert(nowDOM.value)
            }

            inputRef = React.createRef()
            buttonRef = React.createRef()


            render() {
                return (
                    <div>
                        <input ref={this.inputRef} />
                        <input ref={this.inputRef} />
                        <button onClick={this.alertData} ref={this.buttonRef}>click</button>
                    </div>
                )
            }
        }

        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>
相关推荐
哈基米喜欢哈哈哈16 分钟前
Kafka复制机制
笔记·分布式·后端·kafka
麻雀无能为力19 分钟前
python自学笔记14 NumPy 线性代数
笔记·python·numpy
天天进步20152 小时前
从零到一:现代化充电桩App的React前端参考
前端·react.js·前端框架
柯南二号3 小时前
【大前端】React Native Flex 布局详解
前端·react native·react.js
竹杖芒鞋轻胜马,夏天喜欢吃西瓜3 小时前
二叉树学习笔记
数据结构·笔记·学习
知识分享小能手5 小时前
React学习教程,从入门到精通, React教程:构建你的第一个 React 应用(1)
前端·javascript·vue.js·学习·react.js·ajax·前端框架
speop6 小时前
【datawhale组队学习】RAG技术 -TASK05 向量数据库实践(第三章3、4节)
数据库·学习
阿阳微客6 小时前
CSGO搬砖项目详解:从装备选择到市场策略
笔记·学习·游戏
RanceGru6 小时前
神经网络学习笔记11——高效卷积神经网络架构SqueezeNet
笔记·神经网络·学习