React面向组件编程(定义组件,组件三大核心属性,组件事件处理、组件收集表单数据、高阶函数和函数的柯里化)

目录

一、React中定义组件

1、函数式组件

2、类式组件

二、组件三大核心属性

1、组件三大核心属性1: State(状态)

2、组件三大核心属性2: props

3、组件三大核心属性3: ref

 三、组件事件处理

1、事件处理

 四、组件收集表单数据

1、受控组件

2、非受控组件

 五、高阶函数和函数的柯里化

1、高阶函数柯里化

2、不用柯里化实现


一、React中定义组件

1、函数式组件

(1)实质:一个 React 应用就是构建在 React 组件之上的。

(2)函数组件:该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “​props​”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。

(3)代码展示如下

<!-- 准备好一个“容器” -->
	<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.创建函数式组件
		function MyComponent(){
			console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
			return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
		}
		//2.渲染组件到页面
		ReactDOM.render(<MyComponent/>,document.getElementById('test'))
	</script>

(4)注意:

    1.组件名必须首字母大写

    2.虚拟DOM元素只能有一个根元素

    3.虚拟DOM元素必须有结束标签

    4.函数名就是组件名

2、类式组件

(1)含义:

ES6 的 class 来定义组件,通过继承React.Component。解析流程如下:

React解析组件标签,找到了MyComponent组件。

发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。

(2)代码展示

<!-- 准备好一个“容器” -->
	<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 MyComponent extends React.Component {
			render(){
				return <h2>我是用类定义的组件(适用于[复杂组件]的定义)</h2>
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<MyComponent/>,document.getElementById('test'))
	</script>

(3)注意:

    1.组件名必须首字母大写

    2.虚拟DOM元素只能有一个根元素

    3.虚拟DOM元素必须有结束标签

    4.类名就是组件名

二、组件三大核心属性

1、组件三大核心属性1: State(状态)

1.1什么是state

state是组件对象最重要的属性,值是对象(可以包含多个key:value的组合),组件被称为`状态机`,通过更新组件的state来更新对应的页面显示(重新渲染组件),有state称为复杂组件。

1.2 State的用法

State 的使用对象形式(key,value);

代码演示:

 <div id="root"></div>
    <script src="../react.development.js"></script>
    <script src="../react-dom.development.js"></script>
    <script src="../babel.min.js"></script>
    <script type="text/babel">
        class Flag extends React.Component{
            state={
                isflag : false,
            }
            changeFlag=()=>{
                let {isflag,count}= this.state;
                this.setState({isflag:!isflag,count:++count})
                console.log(this.state.isflag);
            }
    
            render() {
                let {isflag,count}= this.state;
                return (
                  <div>
                    <button className={isflag?"yes":"no"}>
                    {isflag?"已关注":"未关注"}                            
                    </button>
                  </div>
                )
            }
        }
        ReactDOM.render(<Flag/>,document.getElementById('root'))
    </script>

这里呢我们只能通过控制台来实现我们想要的已关注和未关注的效果,下面我们来讲一下react绑定事件

 1.3react 绑定事件

react应用中,事件名都是用小驼峰格式进行书写,例如onclick要改写成onClick

 <button className={isflag?"yes":"no"}onClick={this.changeFlag}>
    {isflag?"已关注":"未关注"}
</button>

                注意:

                1、onclick 变为 onClick。

                2、{函数名}返回值给click,加()就会直接调用。

2、组件三大核心属性2: props

        2.1 基础用法

· 每个组件对象都会有props(properties的简写)属性。

· 组件标签的所有属性都保存在props中。

· props 是不可变的,只能通过 props 来传递数据。

        2.2代码展示:

(1)基础用法

<body>
     <!-- 引入react核心库 -->
     <div id="root"></div>
     <!-- 引入react核心库 -->
     <script src="../react.development.js"></script>
     <!-- 引入react-dom,用于支持react操作DOM -->
     <script src="../react-dom.development.js"></script>
     <!-- 引入babel,用于将jsx转为js -->
     <script src="../babel.min.js"></script>
    <script type="text/babel">
        class Person extends React.Component {
            render() {
                let {realname,age} = this.props //props接收参数
                return  (
                    <div>
                        <p>姓名:{realname}</p>
                        <p>年龄:{age}</p>
                    </div>
                ) 
            }

        }
        ReactDOM.render(<Person realname="张三" age={19}/>,document.getElementById("root"))
    </script>
</body>

(2)批量传参

<body>
    <!-- 引入react核心库 -->
    <div id="root"></div>
    <!-- 引入react核心库 -->
    <script src="../react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script src="../react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../babel.min.js"></script>
    <!-- props验证 -->
    <script src="../prop-types.js"></script>
    <script type="text/babel">
        class Person extends React.Component {
            render() {
                let {realname,age} = this.props //props接收参数
                return  (
                    <div>
                        <p>姓名:{realname}</p>
                        <p>年龄:{age}</p>
                    </div>
                ) 
            }
        }
        const p1 = {realname:"张三",age:19} 
         //批量传参
        ReactDOM.render(<Person {...p1}/>,document.getElementById("root"))
    </script>
</body

(3)参数限制

使用prop-types库进限制(需要引入prop-types库)

<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>

	<!-- 引入prop-types,用于对组件标签属性进行限制 -->
	<script type="text/javascript" src="../js/prop-types.js"></script>
		//对标签属性进行类型、必要性的限制
		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
		}
	</script>

3、组件三大核心属性3: ref

定义:组件内的标签可以定义ref来标识自己。

我们做一个小案例来认识一下,我们需要两个input框,中间有个button按钮,点击button按钮,提示左边输入框内的信息,右边input框我们是失去焦点获得信息

如下图:

(1)字符串形式的ref(不推荐使用

<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" name="" placeholder="点击按钮提示数据" />&nbsp;&nbsp;&nbsp;
						<button  onClick={this.showData}>点我提示左侧的数据</button>&nbsp;&nbsp;&nbsp;
						<input ref="input2" onBlur={this.showData2} type="text" name="" placeholder="失去焦点提示数据"/>
					</div>
				)	 
			 }
		 }
		 ReactDOM.render(<Demo/>,document.getElementById('root'))
	</script>

写法就是ref后面加的是内容时引号套起来的内容

 这种方法在React的官网中一说说明了,不推荐使用,后面可能会淘汰掉,所以我们看看其他的方法

(2)回调函数的ref

<script type="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={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
						<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
						<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
					</div>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
	</script>

写法就是ref后面加的是内容时大括号套起来的函数,采用的是先赋值在回调函数

 (3)createRef(目前React最推荐的方式)

<script type="text/babel">
		//创建组件
		class Demo extends React.Component{
			myRef = React.createRef()
			myRef2 = React.createRef()
			//展示左侧输入框的数据
			showData = (e)=>{
				console.log(this.myRef);
				console.log(this.myRef.current);
                console.log(this.myRef.current.value);
				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>

关于createRef我们需要把重点关注在这三个地方

就是创建了一个容器,这个容器挂载了实例的自身上,起了个名字叫myRef或者myRef2

这句话的意思就是因为写了ref ,写的是用createRef创建的容器,就把当前ref所在的节点储存到这个节点中(切记不是回调函数)

 这是第三个重点,我在代码中打印了这三个,那这三个分别是什么意思呢

第一个:打印出来的就是ref给我们创建的节点

第二个:打印出来的就是我们放到节点中的input

第三个:打印出来才是我们想要的结果,curren不能少

 三、组件事件处理

1、事件处理

1. 通过onXxx属性指定事件处理函数(οnclick===>onClick)

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

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

2.当发生事件的事件源与操作源相同时,通过event.target得到发生事件的DOM元素对象。

发生事件源的要操作自身属性的时候 不要用ref

 四、组件收集表单数据

说的收集表单数据我们就要聊到react中的组件,组件分为将状态变化交由React处理的组件和通过ref引用获取的组件两种,前者称为受控组件,后者称为非受控组件,非受控组件的状态在组件自身存储,需要的时候通过ref查询DOM并查找其值。

1、受控组件

(1)受控组件通过props获取其当前值,并通过回调函数(比如onChange)通知变化表单状态发生变化时,都会通知React,将状态交给React进行处理,比如可以使用useState存储

(2)受控组件中,组件渲染出的状态与它的valuechecked属性相对应

(3)受控组件会更新state的流程

<script type="text/babel">
		//创建组件
		class Login extends React.Component{
			state={
				username:"", //用户名
				password:"", // 密码
			}
			saveUsername=(event)=>{
				this.setState({username:event.target.value}) 
			}
			savePassword = (event) =>{
				this.setState({password:event.target.value})
			}
			handleSubmit = (event)=>{
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state 
				alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
			}
			render(){
				return(
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={this.saveUsername} type="text" name="username"/>
						密码:<input onChange={this.savePassword} type="password" name="password"/>
						<button>登录</button>
					</form>
				)
			}
		}
		ReactDOM.render(<Login/>,document.getElementById('test'))
	</script>

代码解析:

代码中有两个输入框,一个的登录框,点击登录展示了输入框的内容,在控制台可以看打随着输入框内容的变化,上方文字相应变化。表单组件Input的值通过onChange回调交给React处理React获取到表单的值(e.target.value)之后,将其保存到了状态变量usernamepassword中界面展示内容通过读取组件状态userename和password,因此userename和password的变化触发组件的重新渲染

2、非受控组件

非受控组件将数据存储在DOM中,而不是组件内,这比较类似于传统的HTML表单元素。

  1. 非受控组件的值不受组件自身的stateprops控制
  2. 非受控组件使用refDOM中获取元素数据
<script type="text/babel">
		//创建组件
		 class Login extends React.Component{
			handleSubmit=(event)=>{
				event.preventDefault(); //阻止表单默认提交
				const {username,password} = this
				alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
			}
			 render() {
				 return(
					 <div>
						<form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
							用户名:<input  ref={c => this.username = c} type="text" name="username"/>
							密码:<input ref={c => this.password =c} type="password" name="password"/>
							<button>登录</button>
						</form>
					 </div>
				 )
			 }
		 }
		//渲染组件
		ReactDOM.render(<Login/>,document.getElementById("test"))
	</script>

代码中有两个输入框,一个的登录框,点击登录展示了输入框的内容,随着输入框内容的变化,上方文字相应变化。

React通过ref获取input元素的值

input值由用户改变,点击提交按钮,在handleSubmit中通过ref获取DOM元素,进而读取输入框的值

输入框的变化并未交由组件控制,而是通过ref获取,也就是直接在DOM中读取

 五、高阶函数和函数的柯里化

1、高阶函数柯里化

定义:如果一个函数符合下面两个规范中的任何一个,那该函数就是高阶函数

·若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数

·若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数

常见的高阶函数有:Promise、setTimeout()、arr.map()等等

说了一大推定义,可能还是看不懂,所以下面直接看代码,在开发的过程总会遇到一种事情,而且很多次。我们来看一下我们上面收集表单数据的写法

 可以看到我们上面的onChange事件写了两次,一次是为用户名的,一次是为密码的。现在还好只有两个,但是我们要是有10个呢?难道我们就要写10次onChange?光是想命名就很难受了,所以我们就有了下面的这两种方法

<script type="text/babel">
		//创建组件
		class Login extends React.Component{
			//初始化状态
			state = {
				username:'', //用户名
				password:'' //密码
			}
			//保存表单数据到状态中
			saveFormData = (dataType)=>{
				console.log(dataType);
				return (event)=>{
					this.setState({[dataType]:event.target.value})
				}
			}
			//表单提交的回调
			handleSubmit = (event)=>{
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state
				alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
			}
			render(){
				return(
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>
						密码:<input onChange={this.saveFormData('password')} type="password" name="password"/>
						<button>登录</button>
					</form>
				)
			}
		}
		//渲染组件
		ReactDOM.render(<Login/>,document.getElementById('test'))
	</script>

这里我们看onChange事件就写一次就可以完成了,是不是方便很多呢。这里saveFormData的返回值依然是个函数,这个函数才是真正onChange的回调。

有人说我不会高阶函数柯里化的写法,有没有别的,当然有的。

2、不用柯里化实现

<script type="text/babel">
		class Login extends React.Component{
			//初始化状态
			state = {
				username:'', //用户名
				password:'' //密码
			}

			//保存表单数据到状态中
			saveFormData = (dataType,event)=>{
				this.setState({[dataType]:event.target.value})
			}
			//表单提交的回调
			handleSubmit = (event)=>{
				event.preventDefault() //阻止表单提交
				const {username,password} = this.state
				alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
			}
			render(){
				return(
					<form onSubmit={this.handleSubmit}>
						用户名:<input onChange={(event)=>{this.saveFormData('username',event)}} type="text" name="username"/>
						密码:<input onChange={event => this.saveFormData('password',event) } type="password" name="password"/>
						<button>登录</button>
					</form>
				)
			}
		}
		//渲染组件
		ReactDOM.render(<Login/>,document.getElementById('test'))

这种就是常规的写法,写个回调然后里面有个函数,通过传入类型和值,去修改相应的state,这个也还是个高阶函数,但是没有了柯里化了。

本篇的内容就到此结束了,本篇篇幅较长,也希望能帮到给为看官们,写的也不是特别详细。希望看官们能留个赞再走哇。最后用一句话结束本篇

                                                                                        ——十年后所有的难过都是下酒菜

猜你喜欢

转载自blog.csdn.net/weixin_52984349/article/details/127575088
今日推荐