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>
相关推荐
用户7678797737329 小时前
后端转全栈之Next.js后端及配置
react.js·next.js
Johnny_FEer11 小时前
什么是 React 中的远程组件?
前端·react.js
艾小码11 小时前
用了这么久React,你真的搞懂useEffect了吗?
前端·javascript·react.js
Olrookie12 小时前
ruoyi-vue(十五)——布局设置,导航栏,侧边栏,顶部栏
前端·vue.js·笔记
江城开朗的豌豆12 小时前
React 跨级组件通信:避开 Context 的那些坑,我还有更好的选择!
前端·javascript·react.js
江城开朗的豌豆13 小时前
Redux 与 MobX:我的状态管理选择心路
前端·javascript·react.js
薛定谔的算法1 天前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
小lan猫1 天前
React学习笔记(一)
前端·react.js
江城开朗的豌豆1 天前
解密React虚拟DOM:我的高效渲染秘诀 🚀
前端·javascript·react.js
前端人类学1 天前
React框架详解:从入门到精通(详细版)
react.js·redux