函数式组件
<div id="test"></div>
<!--babel-->
<script type="text/babel">
//1.创建函数式组件 (必须有返回值)
function MyComponent(){
console.log(this) //undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于简单组件的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent />,document.getElementById('test'))
执行ReactDOM.render之后:
1)react解析组件标签,找到MyComponent组件;
2)发现组件是函数定义,调用该函数,将返回的虚拟DOM转为真实DOM,呈现页面
类相关知识
1.类的创建和实例
<script type="text/javascript">
//创建一个person类
class Person{
//构造器方法
constructor(name,age){
//构造器中的this是类的实例对象
this.name=name
this.age=age
}
//一般方法
speak(){
//speak方法放在类的原型对象上,供实例使用
//通过person实例调用speak时,speak的this就是person实例
console.log(`我叫${this.name},我的年龄是${this.age}`)
}
}
//创建person实例对象
const p1=new Person('Tom',18)
console.log(p1)
const p2=new Person('Jerry',19)
console.log(p2)
p1.speak()
p2.speak()
p1.speak.call({a:1,b:2}) //call更改函数指向 我叫undefined,我的年龄是undefined
</script>
2.继承
<div id="test"></div>
<script type="text/javascript">
//创建一个person类
class Person{
constructor(name,age){
this.name=name
this.age=age
}
speak(){
console.log(`我叫${this.name},我的年龄是${this.age}`)
}
}
//创建一个student类,继承person
class Student extends Person{
constructor(name,age,grade){
super(name,age) //必须写在最开头
this.grade=grade
this.school='第一学校'
}
//重写父类继承过来的方法
speak(){
console.log(`我叫${this.name},我的年龄是${this.age},我读的是${this.grade},我在${this.school}`)
}
study(){
console.log("我很努力的学习")
}
}
//创建student实例对象
const s1 = new Student('小张',19,'高一')
s1.speak() //我叫小张,我的年龄是19,我读的是高一,我在第一学校
s1.study() //我很努力的学习
</script>
3.总结
1)类中构造器不是必须写的,对实例进行一些初始化操作,如添加指定属性时才写;
2)如果A继承B,且A有构造器,则A类构造器中的super必须调用;
3)类中所定义的方法,都是放在类的原型对象上,供实例使用。
4)类中可以直接写赋值语句
类式组件
<div id="test"></div>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
render(){
//render放在类MyComponent的原型对象上,供实例使用
//render中的this是类的实例对象
console.log(this) //MyComponent {…}
return(
<h2>我是用类定义的组件(适用于复杂组件的定义)</h2>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent />,document.getElementById("test"))
</script>
执行ReactDOM.render之后:
1)react解析组件标签,找到MyComponent组件;
2)发现组件是类定义,随后new出来该类的实例,并通过该实例调用到原型上的render方法
3)将render返回的虚拟dom转为真实dom,呈现在页面上
状态state(组件实例的三大核心属性之一)
无状态:简单组件;有状态:复杂组件
1.组件状态驱动页面,数据放在状态中
2.原生事件绑定点击事件
<div>
<button id="button1">按钮1</button>
<button id="button2">按钮2</button>
<button onclick="demo()">按钮3</button>
</div>
<!--babel-->
<script type="text/javascript">
const btn1=document.getElementById('button1');
btn1.addEventListener('click',()=>{
alert('按钮1点击')
})
const btn2=document.getElementById('button2');
btn2.onclick=()=>{
alert('按钮2点击')
}
function demo(){
alert("按钮3点击")
}
</script>
3.实现凉爽和炎热的切换
1)类中this指向问题
①错误代码
<!--babel-->
<script type="text/babel">
//1.创建组件
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state={isHot:true}
}
render(){
console.log(this)
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{ isHot ? '炎热' : '凉爽'}</h1>
}
changeWeather(){
//changeWeather放在weather原型上
//changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用的
//类中的方法默认开启局部严格模式,所以changeWeather中的this为undefined
console.log(this) //undefined
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById("test"))
</script>
②this正确方法(bind)
<script type="text/babel">
//1.创建组件
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state={isHot:true}
this.demo = this.changeWeather.bind(this)
}
render(){
console.log(this)
const {isHot} = this.state
return <h1 onClick={this.demo}>今天天气很{ isHot ? '炎热' : '凉爽'}</h1>
}
changeWeather(){
//获取ishot值 state不可以直接更改(const isHot=this.state.isHot;this.state.isHot =!isHot),需要借助内部API更改(setState)
const isHot=this.state.isHot;
this.setState({isHot:!isHot});
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById("test"))
</script>
2)react中state必须使用setState进行更改,且更新是一种合并,不是直接替换。
在此过程中,类中:constructor只调用1次,render调用1+N(初始+更新状态次数)次,changeWeather调用N(更新状态次数)次
3)精简版
<script type="text/babel">
//1.创建组件
class Weather extends React.Component{
//初始化状态
state={isHot:true,wind:'微风'}
render(){
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{ isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//自定义方法:用赋值语句的形式+箭头函数
changeWeather= ()=>{
console.log(this)
const isHot=this.state.isHot;
this.setState({isHot:!isHot});
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById("test"))
</script>
4.state总结
1)组件中render的this为组件实例对象;
2)自定义的方法的this为undefined,解决(bind或者赋值语句+箭头函数);
3)状态只能通过setState更新。
props
1.基本使用形式
<script type="text/babel">
//创建组件
class Person extends React.Component{
render(){
return (
<ul>
<li>姓名:{this.props.name}</li>
<li>性别:{this.props.age}</li>
<li>年龄:{this.props.sex}</li>
</ul>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Person name="tom" age="18" sex="男"/>,document.getElementById("test"))
</script>
2.展开运算符传值
<div id="test1"></div>
<div id="test"></div>
<script type="text/babel">
//创建组件
class Person extends React.Component{
render(){
// console.log(this)
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//对标签属性进行类型和默认值的设置
Person.propTypes ={
name:PropTypes.string.isRequired, //限制必传和string类型
sex:PropTypes.string,
speak:PropTypes.func
}
Person.defaultProps={
sex:'男', //默认值
age:18
}
const p={name:'老刘',age:19,sex:'女'}
//2.渲染组件到页面
ReactDOM.render(<Person name='jerry' speak={speak}/>,document.getElementById("test1"))
ReactDOM.render(<Person {...p} />,document.getElementById("test"))
function speak(){
console.log("说话")
}
</script>
3.简写形式
class Person extends React.Component{
render(){
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
static propTypes ={
name:PropTypes.string.isRequired, //限制必传和string类型
sex:PropTypes.string,
speak:PropTypes.func
}
static defaultProps={
sex:'男', //默认值
age:18
}
}
4.构造器–几乎用不到
构造器是否接收props,是否传递给super取决于是否希望在构造器中通过this访问props
5.函数式组件
<script type="text/babel">
//创建组件
Person.propTypes ={
name:PropTypes.string.isRequired, //限制必传和string类型
sex:PropTypes.string,
speak:PropTypes.func
}
Person.defaultProps={
sex:'男', //默认值
age:18
}
function Person(props){
const {name,age,sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
//2.渲染组件到页面
ReactDOM.render(<Person name='Jerry'/>,document.getElementById("test"))
</script>
ref
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 type="text" placeholder="点击按钮提示数据" ref="input1"/>
<button onClick={this.showData}>点我提示左侧数据</button>
<input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} ref="input2"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1"/>,document.getElementById('test'))
</script>
2.回调函数形式(内联)
<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 type="text" placeholder="点击按钮提示数据" ref={c=>this.input1=c}/>
<button onClick={this.showData}>点我提示左侧数据</button>
<input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} ref={c=>this.input2=c}/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1"/>,document.getElementById('test'))
</script>
2.回调函数形式(类绑定)
saveInput=(c)=>{
this.input1=c;
console.log("@",c)
}
render(){
const {isHot}=this.state
return(
<div>
<h2>今天天气很{isHot ? '炎热':'凉爽'}</h2>
<input type="text" placeholder="点击按钮提示数据" ref={this.saveInput}/><br />
<button onClick={this.showData}>点我提示数据</button>
<button onClick={this.changeWeather}>点我更换天气</button>
</div>
)
}
}
3.createRef
<script type="text/babel">
//创建组件
class Demo extends React.Component{
//调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框数据
showData= ()=>{
alert(this.myRef.current.value)
console.log(this.myRef) //见下图
}
//展示右侧输入框数据
showData2= ()=>{
alert(this.myRef2.current.value)
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧数据</button>
<input ref={this.myRef2} type="text" placeholder="失去焦点提示数据" onBlur={this.showData2}/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo />,document.getElementById('test'))
</script>
4.ref总结
1)避免使用字符串形式ref;
2)内联和类绑定都可以使用;
3)React.createRef() 是react最推荐的。
事件处理
1.通过onXXX属性指定事件处理函数(注意大小写)
(1)react使用的是自定义(合成)事件,而不是使用原生DOM事件;—为了更好的兼容性
(2)react的是事件是通过事件委托方式处理的(委托的是最外层元素)—为了高效
2.通过event.target得到发生事件的DOM元素(发生事件元素是操作的元素)。—不要过度使用ref
收集表单数据
1.非受控组件:页面中所有输入型DOM都是现用现取
<script type="text/babel">
//创建组件
class Login extends React.Component{
handleSubmit=(event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this;
alert(`用户名是${username.value},密码是${password.value}`)
}
render(){
return(
<form action="#" onSubmit={this.handleSubmit}>
用户名:<input input="text" name="username" ref={c=> this.username=c}/>
密码:<input input="password" name="password" ref={c=>this.password=c}/>
<button>登录</button>
</form>
)
}
}
//渲染页面
ReactDOM.render(<Login />,document.getElementById('test'))
</script>
2.受控组件:页面中所有输入型DOM随着输入维护到state中(vue双向数据绑定)—推荐
<script type="text/babel">
//创建组件
class Login extends React.Component{
state={
username:"", //用户名
password:"" //密码
}
handleSubmit=(event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state;
alert(`用户名是${username},密码是${password}`)
}
//保存用户名
saveUsername=()=>{
this.setState({username:event.target.value})
}
//保存密码
savePassword=()=>{
this.setState({password:event.target.value})
}
render(){
return(
<form action="#" onSubmit={this.handleSubmit}>
用户名:<input input="text" name="username" onChange={this.saveUsername}/>
密码:<input input="password" name="password" onChange={this.savePassword}/>
<button>登录</button>
</form>
)
}
}
//渲染页面
ReactDOM.render(<Login />,document.getElementById('test'))
</script>
高阶函数
高阶函数:一个函数符合下面两个规范中任意一个
(1)A函数接收的参数是一个函数,A为高阶
(2)A函数调用的返回值是一个函数,A为高阶
常见的高阶函数:Promise、setTimeout、arr.map等
1.函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
<script type="text/babel">
//创建组件
class Login extends React.Component{
state={
username:"", //用户名
password:"" //密码
}
handleSubmit=(event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state;
alert(`用户名是${username},密码是${password}`)
}
//保存表单数据
saveFormData=(dataType)=>{
return (event)=>{
this.setState({[dataType]:event.target.value})
}
}
render(){
return(
<form action="#" onSubmit={this.handleSubmit}>
用户名:<input input="text" name="username" onChange={this.saveFormData('username')}/>
密码:<input input="password" name="password" onChange={this.saveFormData('password')}/>
<button>登录</button>
</form>
)
}
}
//渲染页面
ReactDOM.render(<Login />,document.getElementById('test'))
</script>
2.不用柯里化
saveFormData=(dataType,event)=>{
this.setState({[dataType]:event.target.value})
}
render(){
return(
<form action="#" onSubmit={this.handleSubmit}>
用户名:<input input="text" name="username" onChange={event=>this.saveFormData('username',event)}/>
密码:<input input="password" name="password" onChange={event=>this.saveFormData('password',event)}/>
<button>登录</button>
</form>
)
}
}