function Demo() {
return <h2>自定义组件</h2>
}
const VDOM = (
<div>
<h2 className="title" id="{myId}">
<span style={{color:'white'}}>{myData}</span>
</h2>
<Demo></Demo>
</div>
)
ReactDOM.render(VDOM, document.getElementById('test'))
// 1.创建类式组件
class MyConponent extends React.Component {
render() {
// render是放在哪里?MyConponent类的原型对象上,供实例使用
// render中的this是谁?MyConponent的实例对象<=>MyConponent组件实例对象
return <h2>复杂组件</h2>
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyConponent/>, document.getElementById('test'))
/* 执行了ReactDOM.render( .....)之后,发生了什么?
1.React解析组件标签,找到了MyConponent组件
2.发现组件是使用类定义的,随后new出该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面。
*/
需求: 定义一个展示天气信息的组件
// 1.创建类式组件
class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = {
isHot: false,
wind: '小风'
}
// bind()帮助生成新的函数,解决changeWeather中this指向问题
this.changeWeather = this.changeWeather.bind(this)
}
render() {
console.log(this)
const {isHot, wind} = this.state
return <h2 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'寒冷'},有点{wind}</h2>
}
changeWeather() {
// 由于changeWeaather是作为onClick的回调,所以不是通过实例调用的,是直接调用
// 类中的方法默认直接开起了局部的严格模式,所以changeWeather中的this为undefined
console.log(this)
// state状态不可以直接更改
const isHot = this.state.isHot
// 状态必须通过setState进行更新,且更新是一种合并,不是替换
this.setState({isHot: !isHot})
// this.state.isHot = !isHot // 错误写法
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
// 1.创建类式组件
class Weather2 extends React.Component {
constructor(props) {
super(props)
}
state = {
isHot: false,
wind: '小风'
}
render() {
const {isHot, wind} = this.state
return <h2 onClick={this.changeWeather}>今天天气很{isHot?'炎热':'寒冷'},有点{wind}</h2>
}
// 箭头函数没有自己的this,会往外找
// 自定义方法——用赋值语句的形式+箭头函数
changeWeather = ()=> {
const isHot = this.state.isHot
this.setState({isHot: !isHot})
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather2/>, document.getElementById('test'))
需求: 自定义用来显示一个人员信息的组件
类式组件:
class Person extends React.Component {
// 对标签属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired, // 限制name必传,且为字符串
sex: PropTypes.string, // 限制sex为字符串
age: PropTypes.number, // 限制age为数值
speak: PropTypes.func // 限制speak为函数
}
// 指定默认标签属性值
static defaultProps = {
sex: '男',
age: 18
}
render() {
const {name, sex, age} = this.props
// props是只读的
// this.props.name = 'jack' // 此行代码会报错
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="Tom" age={18} speak={speak}/>, document.getElementById('test'))
const p = {name:"Lily",sex:"女",age:18}
ReactDOM.render(<Person {...p}/>, document.getElementById('test2'))
function speak() {
console.log('我会说话')
}
函数式组件:
function Person(props){
const {name, sex, age} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
// 对标签属性进行类型、必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired, // 限制name必传,且为字符串
sex: PropTypes.string, // 限制sex为字符串
age: PropTypes.number, // 限制age为数值
speak: PropTypes.func // 限制speak为函数
}
// 指定默认标签属性值
Person.defaultProps = {
sex: '男',
age: 18
}
ReactDOM.render(<Person name="Tom" age={18} speak={speak}/>, document.getElementById('test'))
const p = {name:"Lily",sex:"女",age:18}
ReactDOM.render(<Person {...p}/>, document.getElementById('test2'))
function speak() {
console.log('我会说话')
}
1.每个组件对象都会有props(properties的简写)属性
2.组件标签的所有属性都保存在props中
内部读取某个属性值
this.props.name
对props中的属性值进行类型限制和必要性限制
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.
}
扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
默认属性值:
Person.defaultProps = {
age: 18,
sex:'男'
}
组件类的构造函数
constructor(props){
// 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
super(props)
console.log(props)//打印所有属性
}
需求: 自定义组件, 功能说明如下:
String类型的ref,过时了,以后可能会被废弃
class Demo2 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"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo2/>, document.getElementById('test'));
回调函数形式的ref:
class Demo3 extends React.Component{
// 展示左侧栏的数据
showData = ()=> {
const {input1} = this
alert(input1.value)
}
// 展示右侧栏的数据
showData2 = ()=> {
const {input2} = this
alert(input2.value)
}
render() {
return (
<div>
<input ref={(currentNode)=>{this.input1 = currentNode}} type="text"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref={c => this.input2 = c} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo3/>, document.getElementById('test'));
createRef创建ref容器:最麻烦的一种,但是是React最推荐的
class Demo4 extends React.Component{
/*
React.createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是专人专用的
*/
myRef = React.createRef()
myRef2 = React.createRef()
showData = ()=> {
alert(this.myRef.current.value)
}
showData2 = ()=> {
alert(this.myRef2.current.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo4/>, document.getElementById('test'));
组件内的标签可以定义ref属性来标识自己
字符串形式的ref
<input ref="input1"/>
回调形式的ref
<input ref={(c)=>{this.input1 = c}}
createRef创建ref容器
myRef = React.createRef()
<input ref={this.myRef}/>
注意:不要过度使用ref
class Demo4 extends React.Component{
myRef = React.createRef()
showData = ()=> {
alert(this.myRef.current.value)
}
showData2 = (event)=> {
alert(event.target.value)
}
render() {
return (
<div>
<input ref={this.myRef} type="text"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
ReactDOM.render(<Demo4/>, document.getElementById('test'));
需求: 定义一个包含表单的组件
输入用户名密码后, 点击登录提示输入信息
非受控组件:
class Login extends React.Component {
handelSubmit = (event)=> {
event.preventDefault();
const {username,password} = this
alert(`你输入的账号是:${username.value},密码是:${password.value}`)
}
render() {
return (
<form action="" onSubmit={this.handelSubmit}>
用户名:<input ref={c=>this.username=c} type="text" name="username"/>
密码:<input ref={c=>this.password=c} type="password" name="password"/>
<button>登陆</button>
</form>
)
}
}
ReactDOM.render(<Login/>, document.getElementById('test'));
受控组件:
class Login2 extends React.Component {
state = {
username: '',
password: ''
}
handelSubmit = (event)=> {
event.preventDefault();
const {username,password} = this.state
alert(`你输入的账号是:${username},密码是:${password}`)
}
saveUsername = (event)=> {
this.setState({username: event.target.value})
}
savePassword = (event)=> {
this.setState({password: event.target.value})
}
render() {
return (
<form action="" onSubmit={this.handelSubmit}>
用户名:<input onChange={this.saveUsername} type="text" name="username"/>
密码:<input onChange={this.savePassword} type="password" name="password"/>
<button>登陆</button>
</form>
)
}
}
ReactDOM.render(<Login2/>, document.getElementById('test'));
优化:使用柯里化
class Login3 extends React.Component {
state = {
username: '',
password: ''
}
handelSubmit = (event)=> {
event.preventDefault();
const {username,password} = this.state
alert(`你输入的账号是:${username},密码是:${password}`)
}
/*
高阶函数:只要符合以下任意一个,就是高阶函数
1.若A函数,接收的参数的是一个函数,那么A函数就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
*/
saveFormData = (dataType)=> {
return (event) => {
console.log(event.target.value)
// obj[key] = value
this.setState({[dataType]: event.target.value})
}
}
render() {
return (
<form action="" onSubmit={this.handelSubmit}>
用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>
密码:<input onChange={this.saveFormData('password')} type="password" name="password"/>
<button>登陆</button>
</form>
)
}
}
ReactDOM.render(<Login3/>, document.getElementById('test'));
函数的柯里化:
function sum(a) {
return (b)=>{
return (c)=>{
return a+b+c
}
}
}
const result = sum(1)(2)(3)
console.log(result) // 6
不用柯里化:
class Login4 extends React.Component {
state = {
username: '',
password: ''
}
handelSubmit = (event)=> {
event.preventDefault();
const {username,password} = this.state
alert(`你输入的账号是:${username},密码是:${password}`)
}
saveFormData = (dataType,event)=> {
this.setState({[dataType]: event.target.value})
}
render() {
return (
<form action="" onSubmit={this.handelSubmit}>
用户名:<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(<Login4/>, document.getElementById('test'));
包含表单的组件分类
需求:定义组件实现以下功能:
class Life extends React.Component {
state = {opacity:0.5}
death = ()=> {
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// 组件挂载完毕
componentDidMount() {
this.timer = setInterval(() => {
let {opacity} = this.state
opacity -= 0.1
if(opacity <= 0) opacity = 1
this.setState({opacity})
}, 200);
}
// 组件将要被卸载
componentWillUnmount() {
clearInterval(this.timer)
}
// render调用的时机:初始化渲染、状态更新之后
render() {
return (
<div>
<h2 style={{opacity:this.state.opacity}}>学不会怎么办</h2>
<button onClick={this.death}>不活了</button>
</div>
)
}
}
ReactDOM.render(<Life/>, document.getElementById('test'));
生命周期的三个阶段(旧)
初始化阶段: 由ReactDOM.render()触发—初次渲染
更新阶段: 由组件内部this.setSate()或父组件重新render触发
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
生命周期的三个阶段(新)
初始化阶段: 由ReactDOM.render()触发—初次渲染
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
static getDerivedStateFromProps(props) {
return props
}
更新阶段: 由组件内部this.setSate()或父组件重新render触发
getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。
class NewList extends React.Component {
state = {newsArr:[]}
componentDidMount() {
setInterval(()=>{
// 获取原状态
const {newsArr} = this.state
const news = '新闻' + (newsArr.length+1)
this.setState({newsArr: [news,...newsArr]})
},1000)
}
getSnapshotBeforeUpdate() {
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps, preState, height) {
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render() {
return (
<div className="list" ref="list">
{
this.state.newsArr.map((n,index)=> {
return <div className="news" key={index}>{n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewList/>, document.getElementById('test'));
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。