React学习(二)----- 面向组件编程
1、React开发工具安装
步骤:谷歌浏览器右上角三个点 ----- 更多工具 ----- 扩展程序 ----- 打开右上角的开发者模式 ----- 页面左上角的首页图标 ----- 下方打开Chrome网上应用店 ----- 搜索React,找到 facebook 出版的开发工具添加
但是浏览器一般无法打开Chrome应用商店,下方提供百度云链接,阅读者下载直接导入即可,导入方式:
步骤:谷歌浏览器右上角三个点 ----- 更多工具 ----- 扩展程序 ----- 打开右上角的开发者模式 ----- 点击加载已解压的扩展程序 ----- 选择下载好的文件夹导入即可 ----- 点击浏览器右上角的图标 ----- 将 React 插件右方的钉子点亮(编写 React 程序时将自动亮起)
地址:链接:https://pan.baidu.com/s/1FGfk2LNNusAYewHsZwlO4w 提取码:hmi1
2、创建组件的两种形式
- 函数式组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>函数式组件</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入核心库 --> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/babel.min.js"></script> <script type="text/babel"> // 创建函数式组件 // 注意:1>调用者:react 2>this指向:undefined,因为script脚本需要babel翻译,翻译后使用了严格模式,而严格 // 模式不允许自定义函数中的this指向window function Demo(){ console.log(this)//undefined return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2> } // 将组件渲染到页面上 ReactDOM.render(<Demo />,document.getElementById("box")) </script> </body> </html>
/*
执行完ReactDOM.render(<Demo />,document.getElementById("box"))之后发生了什么???
1、React解析组件标签,找到Demo组件;
2、发现组件是使用函数定义的,随后调用该函数,将返回的虚拟Dom转换成真实的Dom,随后呈现在页面中;
*/
注意:函数式组件玩儿不了state和refs,只能玩儿一下props
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入核心库 --> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/babel.min.js"></script> <!-- 引入prop-types,用于对组件的标签属性进行限制 ,引入全局对象PropTypes--> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 function Demo(props){ console.log(props) const {name,age,sex} = props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age + 1}</li> </ul> ) } Demo.propTypes = { name:PropTypes.string.isRequired, age:PropTypes.number.isRequired, sex:PropTypes.string } Demo.defaultProps = { sex:'人...' } // 2、将组件渲染到页面上 const obj = {name:"jerry", age:22} ReactDOM.render(<Demo {...obj}/>,document.getElementById("box")) </script> </body> </html>
- 类式组件
- class基础知识
1、构造器 constructor() 不是必须写的,要对实例做一些初始化的操作,如添加指定属性时才写,其中的this是指类的实例对象,constructor() 从创建到销毁,只执行一次;
2、如果A类继承了B类时,且A类中写了构造器,那A类的构造器中super函数必须放在第一行调用;
3、类中定义的方法都是放在了类的原型对象上,供实例去使用;
-
- 例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入核心库 --> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/babel.min.js"></script> <script type="text/babel"> // 1、创建类式组件 class MyComponent extends React.Component{ render(){ return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2> } } console.log(new MyComponent()) // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
render:必须要有一个返回值,返回值是jsx语法,其实也是一个虚拟dom;
1、render 是放在哪里的???----- 类的原型对象上,供实例使用2、实例是谁创建的???----- React内部会创建组件实例对象,调用render()得到虚拟DOM, 并解析为真实DOM3、render 函数中的this是谁??? ----- MyComponent类(组件)的实例对象4、render 函数调用几次???----- 1+n(1是初始化的那次,n是状态更新的次数)/*执行完ReactDOM.render(<MyComponent />,document.getElementById("box"))之后发生了什么???1、React解析组件标签,找到MyComponent组件;2、发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法,将返回的虚拟Dom转换成真实的Dom,随后呈现在页面中;*/
- 例子:
- 复杂组件(有状态)和简单组件(无状态)的区分???
- 组件三大核心属性一:state
- 示例
<script type="text/babel"> // 1、创建一个类式组件 class Weather extends React.Component{ // 定义初始状态 constructor(props){ super(props); // state初始值为null,低版本为一个空对象{} this.state = { isHot : true } } // 定义虚拟dom render(){ const {isHot} = this.state return ( <h2>今天天气很{isHot ? '炎热' : '寒冷'}</h2> ) } } // 2、将组件渲染到页面上 ReactDOM.render(<Weather />,document.getElementById("box")) </script>
- 事件绑定修改state
- 类中定义方法中的this
class Person { constructor(name){ this.name = 'wxh' } speak(){ /* speak放在哪里??? 在类Person的原型上,供实例使用 当 Person 的实例调用 speak 时,speak 里面的this是指 Person 实例 */ console.log(this) console.log(`My name is ${this.name}`) } } const p1 = new Person(); p1.speak()//Person {name: "wxh"} My name is wxh const x = p1.speak; /* this 为什么是undefined,而不是window???----- 因为 class 中定义的方法,内部自动开启了严格模式 'use strict' */ console.log(x())//报错: Cannot read property 'name' of undefined
- 示例:
<script type="text/babel"> // 1、创建一个类式组件 class Weather extends React.Component{ // 定义初始状态 constructor(props){ super(props); // state初始值为null,低版本为一个空对象{} this.state = { isHot : true } } // 定义虚拟dom render(){ const {isHot} = this.state return ( /* 事件处理: 1、React 事件的命名采用小驼峰式(camelCase),而不是纯小写 */ <h2 onClick={this.handlerClick}>今天天气很{isHot ? '炎热' : '寒冷'}</h2> ) } handlerClick(){ /* 1、handlerClick放在哪里??? 在类Weather的原型上,供实例使用 2、当 Weather 的实例调用 handlerClick 时,handlerClick 的this是指 Weather 实例 3、this为什么是undefined???-----由于 handlerClick 是作为 onClick 的回调,所以不是通过实例调用的, 是直接调用,且类中方法默认开启了局部的严格模式,所以是 undefined */ console.log(this) } } // 2、将组件渲染到页面上 ReactDOM.render(<Weather />,document.getElementById("box")) </script>
现象:this为undefined,如何解决???
/* 1、强制绑定this: 通过函数对象的bind()返回一个this为实例对象的新函数 所以点击事件执行时,先到实例自身的属性上去查找,若查找不到,再去类的原型对象上去查找 */ this.handlerClick = this.handlerClick.bind(this);
二、自定义方法 ----- 赋值语句的形式+箭头函数(是定义在实例对象上)
handlerClick= ()=>{this.setState({ isHot:!this.state.isHot }) }
- 事件中state修改
/* 严重注意:
1、状态 state 不可直接修改,要借助一个内置的 API:setState("对象(需要修改的值)","回调函数") 来进行更新,且更新是一种合并,不是替换;
2、回调函数的作用:1>查看数据是否修改成功 2>在当前函数当中获取到最新的DOM结构,相当于nextTick
3、这个函数是个异步的 同步先执行,在执行异步 */ this.setState({ isHot:!this.state.isHot }) - state的简写方式
class Weather extends React.Component{ // 初始化状态,直接定义在实例对象上 state = { isHot : true } // 定义虚拟dom render(){ const {isHot} = this.state return ( <h2 onClick={this.handlerClick}>今天天气很{isHot ? '炎热' : '寒冷'}</h2> ) } }
- 类中定义方法中的this
- 示例
- 组件三大核心属性二:props
- 示例(
)
- 每个组件对象都会有 props(properties的简写) 属性,默认为一个空对象{}
- 组件标签的所有属性都保存在props中
- props是只读的,不允许在组件内部修改,即this.props.name = 'xxx'
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <div id="box1"></div> <!-- 引入核心库 --> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/babel.min.js"></script> <script type="text/babel"> // 1、创建组件 class Dad extends React.Component{ render(){ console.log(this) const {name,age,sex} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } } // 2、将组件渲染到页面上 ReactDOM.render(<Dad name="wxh" age="18" sex="女"/>,document.getElementById("box")) ReactDOM.render(<Dad name="jerry" age="22" sex="男"/>,document.getElementById("box1")) </script> </body> </html>
- 批量传递props
- 扩展运算符
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <div id="box1"></div> <!-- 引入核心库 --> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/babel.min.js"></script> <script type="text/babel"> // 1、创建组件 class Dad extends React.Component{ render(){ console.log(this) const {name,age,sex} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } } // 2、将组件渲染到页面上 ReactDOM.render(<Dad name="wxh" age="18" sex="女"/>,document.getElementById("box")) const obj = {name:"jerry", age:"22", sex:"男"} ReactDOM.render(<Dad {...obj}/>,document.getElementById("box1")) // ReactDOM.render(<Dad name="jerry" age="22" sex="男"/>,document.getElementById("box1")) </script> </body> </html>
注意:
let arr = [1,3,5,7,9] console.log(arr,...arr)//展开一个数组
let arr1 = [1,3,5,7,9] let arr2 = [2,4,6,8,10] // 连接数组,相当于arr1展开 +一个逗号 + arr2展开 console.log(...arr1) console.log([...arr1]) console.log([...arr1,...arr2])
// 在函数中使用:接收不定个数的形参 function sum(...args){ console.log(args)//[1,2,3,4] console.log(...args)//1 2 3 4 return args.reduce((pre,cur)=>{ return pre + cur }) } console.log(sum(1,2,3,4))
const obj = {name:"jerry", age:"22", sex:"男"} console.log(...obj)//报错,展开运算符不能展开一个对象
const obj = {name:"jerry", age:"22", sex:"男",children:{school:'hhh'}} // console.log(...obj)//报错,展开运算符不能展开一个对象 //新语法:字面量的方式复制一个对象,类似于object.assign(),一维属性为深拷贝,二维及以上为浅拷贝 let cloneObj = {...obj} obj.name='wxh' obj.children.school='高一' console.log(obj,cloneObj)
const obj = {name:"jerry", age:"22", sex:"男",children:{school:'hhh'}} console.log({...obj,name:'wxh',address:'地球'})//更新或者合并属性
注意!!!原生JS中{...obj}是一种语法,而React中使用{...obj}和原生JS并不一样,是在差值表达式中使用了...obj,虽然这种写法在原生JS中是不允许的,但是在React+babel的共同处理下,支持这种语法!!!!!
- 扩展运算符
- 对props进行限制
console.log('@@@',...obj);//不允许随便使用,只允许在标签中使用,打印为空 ReactDOM.render(<Dad {...obj}/>,document.getElementById("box1"))
- 组件内部读取某个值:
const {name,age,sex} = this.props
- 对props中的属性值进行类型限制和必要性限制
<!-- 引入prop-types,用于对组件的标签属性进行限制 ,引入全局对象PropTypes v15.5以上--> <script src="../js/prop-types.js"></script> Dad.propTypes = { name:PropTypes.string.isRequired }
- 默认属性值
Dad.defaultProps = { sex:'人...' }
- 组件内部读取某个值:
- props的简写方式,赋值语句是给实例自身添加属性,而static+赋值语句是给类本身添加属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <div id="box1"></div> <!-- 引入核心库 --> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/babel.min.js"></script> <!-- 引入prop-types,用于对组件的标签属性进行限制 ,引入全局对象PropTypes--> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class Dad extends React.Component{ static propTypes = { name:PropTypes.string.isRequired, age:PropTypes.number.isRequired, sex:PropTypes.string } static defaultProps = { sex:'人...' } render(){ const {name,age,sex} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age + 1}</li> </ul> ) } } console.log(PropTypes) // 2、将组件渲染到页面上 const obj = {name:"jerry", age:22, sex:"男"} ReactDOM.render(<Dad {...obj}/>,document.getElementById("box")) ReactDOM.render(<Dad name="wxh" age={18} />,document.getElementById("box1")) </script> </body> </html>
- 示例(
- 组件三大核心属性三:refs(
组件内的标签可以定义ref属性来标识自己
)- 字符串形式的ref(不建议使用) ----- 存在一些问题:https://github.com/facebook/react/pull/8333#issuecomment-271648615 官网也没有给出一个明确的解释,只是说会存在一些效率方面的问题,而且它已过时并可能会在未来的版本中被移除;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入React的核心库 --> <script src="../js/react.development.js"></script> <!-- 引入React的扩展库,用来渲染虚拟DOM --> <script src="../js/react-dom.development.js"></script> <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 --> <script src="../js/babel.min.js"></script> <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 --> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class MyComponent extends React.Component{ // 对标签属性的类型、必要性进行控制 static propTypes = { } // 对标签属性的默认值进行控制 static defaultProps = { } // 初始化数据 state = { } render(){ return( <div> <input type="text" placeholder="点击按钮提示数据" ref="leftInput"/> <button id="btn" onClick={this.handlerClick}>点我提示左侧的数据</button> <input type="text" placeholder="失去焦点提取数据" onBlur={this.handlerBlur} ref="rightInput"/> </div> ) } // 展示左侧输入框的数据 字符串形式 handlerClick = ()=>{ console.log(this.refs.leftInput) let TDOM = this.refs.leftInput; console.log(TDOM.value) } // 失去焦点显示数据 字符串形式 handlerBlur = ()=>{ const {rightInput} = this.refs; console.log(rightInput.value) } } // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
- 回调形式的ref
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入React的核心库 --> <script src="../js/react.development.js"></script> <!-- 引入React的扩展库,用来渲染虚拟DOM --> <script src="../js/react-dom.development.js"></script> <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 --> <script src="../js/babel.min.js"></script> <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 --> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class MyComponent extends React.Component{ // 对标签属性的类型、必要性进行控制 static propTypes = { } // 对标签属性的默认值进行控制 static defaultProps = { } // 初始化数据 state = { } render(){ return( <div> <input type="text" placeholder="点击按钮提示数据" ref={(currentNode)=>{this.leftInput = currentNode; console.log('@',currentNode)}}/> <br /><br /> <button id="btn" onClick={this.handlerClick}>点我提示左侧的数据</button> <br /><br /> <input type="text" placeholder="失去焦点提取数据" onBlur={this.handlerBlur} ref={currentNode => this.rightInput = currentNode} /> </div> ) } // 展示左侧输入框的数据 字符串形式 handlerClick = ()=>{ const {leftInput} = this console.log(leftInput.value) } // 失去焦点显示数据 字符串形式 handlerBlur = ()=>{ const {rightInput} = this; console.log(rightInput.value) } } // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
解决方式:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入React的核心库 --> <script src="../js/react.development.js"></script> <!-- 引入React的扩展库,用来渲染虚拟DOM --> <script src="../js/react-dom.development.js"></script> <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 --> <script src="../js/babel.min.js"></script> <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 --> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class MyComponent extends React.Component{ // 对标签属性的类型、必要性进行控制 static propTypes = { } // 对标签属性的默认值进行控制 static defaultProps = { } // 初始化数据 state = { isHot : true } render(){ const {isHot} = this.state return( <div> <h2 onClick = {this.changeState}>今天天气好{isHot ? '晴朗' : '凉爽'}</h2> {/*<input type="text" placeholder="点击按钮提示数据" ref={(currentNode)=>{this.leftInput = currentNode; console.log('@',currentNode)}}/> */} <input type="text" placeholder="点击按钮提示数据" ref={this.saveInputRef}/> <br /><br /> <button id="btn" onClick={this.handlerClick}>点我提示左侧的数据</button> <br /><br /> <input type="text" placeholder="失去焦点提取数据" onBlur={this.handlerBlur} ref={currentNode => this.rightInput = currentNode} /> </div> ) } // 解决更新时执行两次的问题 saveInputRef = (currentNode)=>{ this.leftInput = currentNode; console.log('@',currentNode) } // 修改标识 changeState = () =>{ this.setState({ isHot : !this.state.isHot }) } // 展示左侧输入框的数据 字符串形式 handlerClick = ()=>{ const {leftInput} = this console.log(leftInput.value) } // 失去焦点显示数据 字符串形式 handlerBlur = ()=>{ const {rightInput} = this; console.log(rightInput.value) } } // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
注意:回调 ref 中调用次数的问题??? ----- 调用者:React 如果 ref 回调函数是以内联函数的方式定义的,在更新(render调用)过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,
所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
- 字符串形式的ref(不建议使用) ----- 存在一些问题:https://github.com/facebook/react/pull/8333#issuecomment-271648615 官网也没有给出一个明确的解释,只是说会存在一些效率方面的问题,而且它已过时并可能会在未来的版本中被移除;
- 组件三大核心属性一:state
-
-
- createRef创建ref容器(推荐使用)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入React的核心库 --> <script src="../js/react.development.js"></script> <!-- 引入React的扩展库,用来渲染虚拟DOM --> <script src="../js/react-dom.development.js"></script> <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 --> <script src="../js/babel.min.js"></script> <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 --> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class MyComponent extends React.Component{ // 对标签属性的类型、必要性进行控制 static propTypes = { } // 对标签属性的默认值进行控制 static defaultProps = { } // 初始化数据 state = { } /* React.createRef 调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点,该容器是“专人专用”,只能存一个 */ myRef = React.createRef() render(){ console.log(this) return( <div> <input type="text" ref={this.myRef} placeholder="失去焦点提取数据" onBlur={this.handlerBlur}/> </div> ) } // 失去焦点显示数据 字符串形式 handlerBlur = ()=>{ const {current} = this.myRef; console.log(current.value) } } // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
注意:
React.createRef 调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点,该容器是“专人专用”,只能存一个
- createRef创建ref容器(推荐使用)
-
3、React中的事件处理
- 示例
render(){ return( <div> <input type="text" placeholder="失去焦点提取数据" onBlur={this.handlerBlur} ref={currentNode => this.rightInput = currentNode} /> </div> ) } /* 1.通过onXxx属性指定事件处理函数(注意大小写) 1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ----- 为了更好的兼容性 2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ----- 为了更高效 2.通过event.target得到发生事件的DOM元素对象 ----- 不要过度使用ref */ // 失去焦点显示数据 字符串形式 handlerBlur = (e)=>{ console.log(e.target) console.log(e.target.value) }
/*1.通过onXxx属性指定事件处理函数(注意大小写)1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ----- 为了更好的兼容性2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ----- 为了更高效2.通过event.target得到发生事件的DOM元素对象 ----- 不要过度使用ref*/
4、收集表单数据(包含表单的组件分类)
- 非受控组件:表单中所有输入类 DOM 的值,现用现取,即点击按钮的时候才收集
- Ref的形式
<!-- * @Descripttion: * @version: * @Author: 北栀女孩儿 * @Date: 2021-08-30 09:28:15 * @LastEditTime: 2021-08-30 09:52:22 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入React的核心库 --> <script src="../js/react.development.js"></script> <!-- 引入React的扩展库,用来渲染虚拟DOM --> <script src="../js/react-dom.development.js"></script> <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 --> <script src="../js/babel.min.js"></script> <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 --> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class MyComponent extends React.Component{ // 对标签属性的类型、必要性进行控制 static propTypes = { } // 对标签属性的默认值进行控制 static defaultProps = { } // 初始化数据action="http://www.atguigu.com" state = { } render(){ return( <div> <form method="post" onSubmit={this.handlerSubmit}> <label htmlFor="userName">用户名:</label> <input type="text" id="userName" name="userName" ref={currentNode => this.userName = currentNode}/> <br /> <br /> <label htmlFor="password">密码:</label> <input type="password" id="password" name="password" ref={currentNode => this.password = currentNode}/> <br /> <br /> <button>提交</button> </form> </div> ) } // 处理提交的按钮 handlerSubmit = (e) =>{ // 阻止表单提交 e.preventDefault() const {userName,password} = this; alert(`你输入的用户名是:${userName.value},密码是${password.value}`) } } // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
- Ref的形式
- 受控组件(推荐使用):表单中所有输入类 DOM 的值,随着输入将值维护到状态state中,即vue中的数据双向数据绑定
<!-- * @Descripttion: * @version: * @Author: 北栀女孩儿 * @Date: 2021-08-30 09:28:15 * @LastEditTime: 2021-08-30 10:09:48 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 容器 --> <div id="box"></div> <!-- 引入React的核心库 --> <script src="../js/react.development.js"></script> <!-- 引入React的扩展库,用来渲染虚拟DOM --> <script src="../js/react-dom.development.js"></script> <!-- 引入babel,用来将jsx转换为浏览器可以识别的文件 --> <script src="../js/babel.min.js"></script> <!-- 引入propTypes,对标签属性的类型、必要性以及默认值进行控制 --> <script src="../js/prop-types.js"></script> <script type="text/babel"> // 1、创建组件 class MyComponent extends React.Component{ // 对标签属性的类型、必要性进行控制 static propTypes = { } // 对标签属性的默认值进行控制 static defaultProps = { } // 初始化数据 state = { userName :'', password : '' } render(){ return( <div> <form action="" method="get"> <label htmlFor="userName">用户名:</label> <input type="text" id="userName" name="userName" onChange={this.handlerUserName}/> <br /> <br /> <label htmlFor="password">密码:</label> <input type="password" id="password" name="password" onChange={this.handlerPassword}/> <br /> <br /> <button onClick={this.handlerSubmit}>提交</button> </form> </div> ) } // 监听 input 的输入事件 ,保存输入值到状态中 handlerUserName = (e) => { console.log(e.target.value) this.setState({ userName : e.target.value }) } handlerPassword = (e) =>{ console.log(e.target.value) this.setState({ password : e.target.value }) } // 处理提交的按钮 handlerSubmit = (e) =>{ e.preventDefault() const {userName,password} = this.state alert(`你输入的用户名是:${userName},密码是${password}`) } } // 2、将组件渲染到页面上 ReactDOM.render(<MyComponent />,document.getElementById("box")) </script> </body> </html>
- 扩展:高阶函数和函数的柯里化
- 传参:错误的方式
<label htmlFor="userName">用户名:</label> // 这么写的含义是将 this.saveFormData函数的返回值 作为onChange事件的回调函数,即把 undefined 作为回调,input 输入值发生变化时不在执行!!! // 而不是将 this.saveFormData函数 作为回调 <input type="text" id="userName" name="userName" onChange={this.saveFormData('userName')}/> // 监听 input 的输入事件 ,保存输入值到状态中 saveFormData = (e) => { console.log(e.target.value) }
- 正确的方式:高阶函数的方式
<label htmlFor="userName">用户名:</label> <input type="text" id="userName" name="userName" onChange={this.saveFormData('userName')}/> // 监听 input 的输入事件 ,保存输入值到状态中 saveFormData = (dataType) => { // 返回的函数将是真正的 onChange 事件的回调函数 return (event) => { console.log(dataType,event.target.value) this.setState({ [dataType] : event.target.value }) // 错误的方式:dataType这个时候是一个字符串,相当于'dataType',属性为dataType,而不是变量 // this.setState({ // dataType : event.target.value // }) }
}高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
1)若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数;
2)若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数;
常见的高阶函数:Promise、定时器、一些数组方法(map、filter)等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
- 传参:错误的方式
function sum(a){ return (b) => { return (c) => { return a+b+c } }
}
console.log(sum(1)(2)(3))//6
-
- 正确的方式:非高阶函数的方式
<label htmlFor="userName">用户名:</label> <input type="text" id="userName" name="userName" onChange={(event)=>{this.saveFormData('userName',event.target.value)}}/> // 监听 input 的输入事件 ,保存输入值到状态中 saveFormData = (dataType, value) => { console.log(dataType, value) this.setState({ [dataType] : value }) }
- 正确的方式:非高阶函数的方式
5、遇到的问题
后续补充