component - class
- 知识回顾(类的继承)
01 super(props) super有参数
class A{
constructor(n){
this.x = n
}
}
class B extends A{
constructor(props){
super(props)
this.y = 100
}
}
let b = new B(100)
console.log(b.x);
console.dir(b);
????????02 super( )无参数时
- 定义类组件:有自己的状态 和 生命周期
<!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="app"></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">
class Welcome extends React.Component{
constructor(props){
console.log(props);
super(props)
}
render(){
console.log(this);
console.log(this.props);
return <h1>Hello World!</h1>
}
}
const e = <Welcome name = "tina" />
console.log(e);
ReactDOM.render(e,document.getElementById("app"));
</script>
</body>
</html>
注意:
即使不在constructor中调用super(props),render中依然可以使用this.props,因为render可以帮我们做; 那么,在constructor中调用super(props)有什么意义呢? 在constructor中调用super(props),可以在constructor阶段直接使用this.props 例如:super()不传参数,在constructor阶段中打印this.props结果为undefined
- 时钟案例
01 函数组件(手动)
function Clock(props){
return (
<div>
<h1>Clock</h1>
<h2>{props.time}</h2>
</div>
)
}
function tick(){
var e = <Clock time={new Date().toLocaleTimeString()}/>
ReactDOM.render(e,document.getElementById("app"));
}
setInterval(tick,1000)
????????02 函数组件转化为类组件(手动)
class Clock extends React.Component{
render(){
console.log(this.props);
return(
<div>
<h1>Clock</h1>
<h2>{this.props.time}</h2>
</div>
)
}
}
function tick(){
var e = <Clock time={new Date().toLocaleTimeString()}/>
ReactDOM.render(e,document.getElementById("app"));
}
setInterval(tick,1000)
注意:
当我们在render中打印this.props的时候,会发现它会一直重复打印,这是为什么呢? ——这是因为render具有该特点: 每次props变化,render会重新执行,页面会重新渲染;
????????03 自动化时钟(维护状态state)
????????当我们在componentDidMount(){}钩子函数中,用this.state.time=""改变数据时,会发现,打印出的数据被成功修改,但是页面并没有更新,这是react不同于vue之处,它不具备双向绑定。 解决方法:强制更新 (1)使用forceUpdate()强制刷新,每次刷新都会重新执行 (2)使用this.setState()也会重新执行(推荐使用)
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time : new Date().toLocaleTimeString(),
}
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
this.timerId=setInterval(()=>{
this.setState({time:new Date().toLocaleTimeString()})
},1000)
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
- 类组件的this指向问题
01 生命周期中的钩子函数中的this - -> 指向当前类组件的实例(如:componentDidMount,render)
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time : new Date().toLocaleTimeString(),
}
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
console.log(this);
this.timerId=setInterval(()=>{
this.setState({time:new Date().toLocaleTimeString()
})
},1000)
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
????????02 函数的this --> 指向undefined
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time : new Date().toLocaleTimeString(),
}
}
go(){
console.log(this);
this.setState({
time : new Date().toLocaleTimeString(),
})
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={this.go}>GO1</button>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
????????03 this.go 与 this.go()
this.go --> 表示引用 (this指向undefined); this.go()–> 找到函数,直接执行(报错,会导致内存溢出<–重复调用render和执行函数–死循环,this指向调用者);
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time : new Date().toLocaleTimeString(),
}
}
go(){
console.log(this);
this.setState({
time : new Date().toLocaleTimeString(),
})
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={this.go()}>GO2</button>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
????????04 关于函数this指向问题的解决方法 ????????(1)使用箭头函数:箭头函数中的this指向该箭头函数所在的外围作用域,即render,render --> 指向当前类组件的实例; ????????*问题:
- 函数是一个特殊对象,需要占用内存空间;每次调用render()都i相当于重复声明 => 重复开辟内存空间,性能相对较差;
- jsx元素构建中添加js逻辑代码,导致代码复杂、臃肿,不好维护
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time : new Date().toLocaleTimeString(),
}
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={
()=>{
this.setState({
time : new Date().toLocaleTimeString(),
})
}
}>GO3</button>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
????????(2)手动改变this指向,使用bind进行绑定; ????????*注意:该方法虽然解决了代码逻辑混乱问题,但是每次重新绑定都会生成新的函数,依然没有解决重复开辟内存空间,性能相对较差的问题
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {
time : new Date().toLocaleTimeString(),
}
}
go(){
console.log(this);
this.setState({
time : new Date().toLocaleTimeString(),
})
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={this.go.bind(this)}>GO4</button>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
????????(3)推荐方式:在constructor构造函数中,一次性绑定this,在render中直接通过this.go进行调用
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {time : new Date().toLocaleTimeString()}
console.log(this);
this.go = this.go.bind(this)
}
go(){
console.log("GO5");
this.setState({
time : new Date().toLocaleTimeString(),
})
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={this.go}>GO5</button>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
????????(4)ES7新的函数写法go=()=>{},不需要考虑this , 可以在render中直接通过this.go进行调用
class Clock extends React.Component{
constructor(props){
super(props)
this.state = {time : new Date().toLocaleTimeString()}
console.log(this);
this.go = this.go.bind(this)
}
go=()=>{
console.log(this);
this.setState({
time : new Date().toLocaleTimeString(),
})
}
render(){
return(
<div>
<h1>Clock</h1>
<h2>{this.state.time}</h2>
<button onClick={this.go}>GO6</button>
<button onClick={
()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
}>remove Component</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
}
componentWillUnmount(){
clearInterval(this.timerId)
}
}
ReactDOM.render(<Clock />,document.getElementById("app"));
- setState方法
01 状态初始化的两种方式
<script type="text/babel">
class App extends React.Component{
constructor(props){
super(props)
this.state = {a:0}
}
state1 = {b:0,c:0}
render(){
return (
<div>
<h1>a:{this.state.a}</h1>
<h1>b:{this.state1.b}</h1>
<h1>c:{this.state1.c}</h1>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
????????02 可以直接修改数据,但是不能触发重新渲染render
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
this.state.a=1
console.log(this.state.a);
}
render(){
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
????????03 setState()修改数据 + 触发render
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
this.setState({
a:1
})
console.log(this.state.a);
}
render(){
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
componentDidMount(){
this.setState({
b:1
})
console.log(this.state.b);
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
??注意:
- 当我们打开页面的时候,页面中的b直接变成了1,打印的结果是0,这是因为setState在生命周期钩子函数中是异步的,先执行console.log(),再执行this.setState();
- 当我们第一次点击按钮的时候,会发现页面中a变成了1,而打印时却是0,这是因为setState在合成事件(handleClick)里面是异步的
- 总结:
setState是异步的条件:1)在合成事件中 ????2)在生命周期钩子函数中
????????04 多次调用setState
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
this.setState({a:1})
this.setState({b:1})
this.setState({c:1})
console.log(this.state);
}
render(){
console.log('render');
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
componentDidMount(){
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
- 打印this.state结果为{a: 0, b: 0, c: 0},因为setState是异步的
- 点击按钮后,三次setState,只打印一次render <=react性能优化 state数据全部被修改,(批量更新)
- react性能优化,批量更新的时机是在render之前的某个时候,setState都已经更新完才会打印render
????????05 累加
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
this.setState({a:1})
this.setState({a:this.state.a+1})
this.setState({a:this.state.a+1})
}
render(){
console.log('render');
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
componentDidMount(){
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
- 点击按钮后,页面中a变成了1,并没有进行多次累加=>因为setState是异步的,不是立即执行的;
- 执行机制如下:
this.setState({a:1}) //a:1 不立即执行 this.setState({a:this.state.a+1})//a:0+1=1 不立即执行 this.setState({a:this.state.a+1})//a:0+1=1 不立即执行 //发现都为a:1,所以进行合并,转换为 this.setState({a:1}) 因此最后a为1 - 隐患:调用setState更新的值,依赖于state里面的值,不能依赖于上一个状态去计算下一个状态
????????06 解决05的累加隐患问题
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
this.setState((preState,props)=>{return{a:preState.a + 1}})
this.setState((preState,props)=>{a:preState.a + 1})
this.setState((preState,props)=>{a:preState.a + 1})
this.setState((preState,props)=>{a:preState.a + 1})
}
render(){
console.log('render');
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
componentDidMount(){
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
- setState 接收一个函数
- 函数的第一个参数preState=>代表上一个状态 - 函数的返回值也是一个对象,是新的状态,可以依赖于上一个状态做计算 - 并未解决异步问题
点击按钮a由之前的0,变成了1,再次点击变成2,只实现了依赖上一次的结果计算;而代码中有四个setState,并没有累加4,因此没有解决异步问题
????????07 解决setState在合成事件中的异步问题
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
this.setState((preState,props)=>({a:preState.a + 1}),()=>{
console.log('callback',this.state.a);
})
}
render(){
console.log('render');
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
componentDidMount(){
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
this.setState((preState,props)=>({a:preState.a + 1}),()=>{ console.log(‘callback’,this.state.a); }) 通过第二个参数,通过回调函数获取state的真实值
????????08 定时器中的setState是同步的
<script type="text/babel">
class App extends React.Component{
state = {a:0,b:0,c:0}
handleClick=()=>{
console.log(this);
setTimeout(()=>{
this.setState({a:1})
this.setState({a:this.state.a+1})
this.setState({a:this.state.a+1})
},1000)
}
render(){
console.log('render');
return (
<div>
<h3>a:{this.state.a}</h3>
<h3>b:{this.state.b}</h3>
<h3>c:{this.state.c}</h3>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("app"));
</script>
- 点击一次按钮,a变成了3,可见定时器中的setState是同步的,并且不会发生合并
- 每次render都会执行,不会批量更新,没有状态依赖问题
- 异步问题
01 同步
function add(a,b){
return a + b
}
console.log(add(1,1));
????????02 含有异步代码
function sum(a,b){
setTimeout(()=>{
return a + b
},1000)
}
console.log(sum(1,1));
????????03 解决异步问题(1)callback
- 回调函数callback(在函数的参数中传递一个函数,并在指定的时机去执行)
- 问题:会导致回调地狱,不停的嵌套回调函数
function sumer(a,b,callback){
setTimeout(()=>{
let result = a + b
callback(result)
},1000)
}
function callback(result){
console.log(result);
}
let r = sumer(1,2,callback);
console.log(r);
????????03 解决异步问题(2)封装promise–>解决了回调地狱的问题
function sumers(a,b){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let result = a + b
resolve(result)
},1000)
})
}
sumers(10,10)
.then((result)=>{
console.log(result);
})
.catch((err)=>{
console.log(err);
})
|