一丶setState更新状态
this.stState基于当前值更新UI状态,不能使用this.state.count += 1 或 ++ 的写法
import { Component } from 'react'
export default class App extends Component {
// 提供状态
state = {
count: 0
}
// 事件处理程序
handleClick = () => {
// 更新状态 10
// this.setState({
// count: 10
// })
// +1
this.setState({
// 基于当前状态值来更新状态:
count: this.state.count + 1
})
}
render() {
console.log('render')
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
二丶组件通讯
1.父子组件通讯
父级到子级:函数式组件通过参数来接收,class类组件通过this.props来接收
props进阶用法:
1.props有一个children属性,props.children=当前标签页中间的值,children中间可写入表达式
2.props校验,需要安装引入 import PropTypes from "prop-types" 校验包,使用方法:组件名.PropTypes={ 值:PropTypes.类型 },如:List.PropTypes={ name:PropTypes.Array },
常见的类型有:array,bool,func,number,object,string,
React元素类型:element ,
必填项:isRequired
特定结构的对象:shape({ })
默认值:defaultProps
import { Component } from 'react'
// 函数组件
// 组件接收数据:通过 参数props 来接收
// props 是一个对象
const Hello = props => {
console.log('接收到传递给组件的数据:', props)
return <div>Hello 组件</div>
}
class Hello1 extends Component {
render() {
console.log('接收属性:', this.props)
return <div>Hello1 组件</div>
}
}
export default class App extends Component {
render() {
return (
<div>
{/* 给组件传递属性 */}
<Hello name="jack" age="19"></Hello>
{/* 给 类组件 传递属性 */}
<Hello1 name="jack" age="19"></Hello1>
{/* <div id=""></div> */}
</div>
)
}
}
子级到父级:通过触发绑定在子级标签上的事件来传递参数
import { Component } from "react";
// 创建父组件
class Parent extends Component {
state = {
money: 10000,
car: "二手-奥拓",
};
// 1 父组件提供一个回调函数
buyPhone = (price) => {
// console.log('父组件接收到子组件传递过来的数据:', price)
this.setState({
money: this.state.money - price,
});
};
render() {
return (
<div>
<h1>我是父组件:</h1>
<Child
money1={this.state.money}
car1={this.state.car}
// 2 将回调函数传递给 子组件
buyPhone1={this.buyPhone}
/>
</div>
);
}
}
// // 创建子组件
// const Child = props => {
// // console.log('子组件:', props)
// return (
// <div>
// 我是子组件 - {props.money1} - {props.car1}
// <div>
// {/* 3 子组件调用父组件传递过来的 回调,并且将要传递的数据,作为参数传递 */}
// <button onClick={() => props.buyPhone1(100)}>买手机</button>
// </div>
// </div>
// )
// }
class Child extends Component {
render() {
const { money1, car1, buyPhone1 } = this.props;
console.log("子组件", money1);
return (
<div>
我是子组件 - {money1} - {car1}
<div>
<button onClick={() => buyPhone1(200)}>买手机</button>
</div>
</div>
);
}
}
export default class App extends Component {
render() {
return (
<div>
<Parent />
</div>
);
}
}
2.兄弟组件通讯
创建一个中转父组件,通过子传父向父级传递参数, 父级拿到数据后,通过父传子传递,实现兄弟组件通讯
3.跨组件通讯(Context)
通过react的creactContext 来进行跨组件通讯,实现思路:先将creactContext进行结构,解构Provider(提供数据),Consumer(消化数据)两个属性,Provider提供value属性,来进行传值,Consumer中获取value数据,需要在标签包裹中创建一个表达式,写入一个回调函数,其中回调函数的参数就是当前value值
import React, { Component, createContext } from "react";
import "./App.css";
// Context 跨组件传递数据:
// 目标:将 App 组件中的状态数据,传递给 Child 组件
const { Provider, Consumer } = createContext();
const Node = () => (
<div className="node">
Node
<SubNode />
</div>
);
const SubNode = () => (
<div className="sub-node">
SubNode
<Consumer>
{(color) => (
<ul style={{ color }}>
<li>{color}</li>
</ul>
)}
</Consumer>
<Child />
</div>
);
// 需要接收 App 组件中共享的数据
const Child = () => (
<div className="child">
Child
<Consumer>
{(color) => <p style={{ color }}>接收到数据:{color}</p>}
</Consumer>
</div>
);
export default class App extends Component {
state = {
color: "blue",
};
render() {
return (
<Provider value={this.state.color}>
<div className="app">
<h1>我是App组件</h1>
<Node />
</div>
</Provider>
);
}
}
三丶生命周期
React生命周期分为三个阶段:挂载(实例化期)、更新(存在期)、卸载(销毁期)。
1.挂载期:一个组件实例初次北创建的过程。
2.更新期:组件在创建后再次渲染的过程。
3.卸载期:组件在使用完后被销毁的过程。
挂载阶段
触发的钩子函数:
1.constructor: 在创建组件时触发,可以初始化state中的数据, 为事件绑定this
2.render: 每次组件渲染(初次渲染组件和更新组件)都会被触发;作用是渲染UI; 不能够调用 setState 因为setState会更新数据,这样会导致递归渲染数据
3.componentDidMount::DOM已经渲染完成;可以进行DOM操作和网络请求
更新阶段
触发的钩子函数:componentDidUpdate
此钩子函数为更新阶段触发的钩子函数,有一个参数prevProps,是上一个props的值,一般用来判断是否需要更新数据,调用时会先触发render渲染函数,之后在触发更新的钩子函数。
如果要调用:setState()更新状态或网络请求,必须包放一个if语句;否则会导致递归更新; 因为调用 setState会触发render函数;render触发后,就会触发componentDidUpdate; 这样就导致了-递归更新,
更新组件的几种情况:
1.组件接收到一个新的属性,会触发render函数
2.调用this.setState更新数据时,会触发render函数
3.调用forceupdata(),强制刷新页面时,会触发render函数
卸载阶段
触发的钩子函数:componentWillUnmount
组件将要卸载的时候会被触发,可以做清除定时器
四丶setState进阶用法
1.setState延迟更新(批量更新):
执行过程:react 框架内部有一个表示批量更新的标记 isBatchingUpdates,当我们点击按钮触发了 react 自己的事件后,这个标记就被修改为 true,表示批量更新,所以,此时调用 setState 的时候,并不会立即更新状态,而是存储到了一个队列中,将来等到所有的操作都执行完成后,React 就会合并所有的状态更新,一次性的更新状态,最终,将 isBatchingUpdates 标记设置为 false
import { Component } from 'react'
class Counter extends Component {
state = {
count: 0
}
handleClick = () => {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 2
})
this.setState({
count: this.state.count + 3
})
console.log('count:', this.state.count) // 结果为3
}
render() {
console.log('render')
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>打豆豆</button>
</div>
)
}
}
export default class App extends Component {
render() {
return (
<div>
<Counter />
</div>
)
}
}
2.改变state立即生效(每次执行拿到prevState最新的值,依次执行,返回数据渲染)
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
this.setState(prevState => {
return {
count: prevState.count + 2
}
})
this.setState(prevState => {
return {
count: prevState.count + 3
}
})
console.log('count:', this.state.count) // 结果为6
3.setState同步与异步
同步的情况也就是setState延迟更新(批量更新)的方式
异步方式:每次调用 setState 都会立即更新状态,并且让组件重新渲染,因为 定时器 中的代码是异步代码,定时器中的代码不会被立即执行而是放到了事件队列中,事件队列中的代码,会在 JS 主线程(同步代码)都执行完成后,再执行。当主线程中的代码执行完成后,React 已经将 isBatchingUpdates 标记设置为 false,此时,再调用 setState(),此时,因为批量合并更新的标记已经是 false,所以,React 会调用一次 setState 就立即更新一次状态,并且立即渲染
setTimeout(() => {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 2
})
}, 0)
解决setTimeout中不断渲染的问题:
通过一个 unstable_batchedUpdates API 可以让 setState,在定时器中执行,也跟正常情况表现一致了,也就是:批量合并更新
setTimeout(() => {
ReactDOM.unstable_batchedUpdates(() => {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 2
})
})
}, 0)