二、class组件
1.语法
在class里的render()函数内返回dom结构
<div id="app"></div>
....
<script type="text/babel">
class 组件名 extends React.Component{
render(){
return (<h1>你好<h1/>)
}
}
ReactDOM.render(<组件名 />, document.querySelector('#app'))
<script/>2.状态state
1.概念
react组件中的状态,用来存放数据,相当于vue组件中的data模型数据
2.定义
使用类组件需要继承React.Component类
<div id="app"></div>
....
<script type="text/babel">
class 组件名 extends React.Component{
//写法一
// constructor(props){
// super(props)
// this.state={
// name:'夏至'
// }
// }
//写法二,推荐
state:{
name:'夏至'
}
render(){
return (<h1>你好<h1/>)
}
}
ReactDOM.render(<组件名 />, document.querySelector('#app'))
<script/>3.获取
<h1>{this.state.键}</h1>
//可以使用解构赋值
const {键} =this.state
<h1>{键}</h1>4.修改
**注意:**setState是异步的
- callback可选,在值被修改后触发
// 写法一
this.setState({
键1:值1,
// ...
} [, callback])
// 写法二
// oldState为旧数据
this.setState((oldState)=>{
return{
键:值
}
}[,callback])3.属性props
1.概念
父组件调用子组件时,给子组件添加的属性会在子组件的props上
2.语法
切记切记props只读不能修改
class 组件名 extends React.Component
{
render() {
console.log(this.props) // {a:1, b:2}
//子组件接收
const {a,b}=this.props
return (
<h1>{a}</h1>
<h1>{b}</h1>
)
}
}
//父组件传值
<组件名 a="1" b="2" />3.验证
在class里写
1.接收属性,并且设置默认值
class 组件名 extends React.Component
{
static defaultProps = {
a: false,
b: 2,
}
//...
}2.给传过来的属性添加验证规则
(1)限制数据类型
static propTypes={
属性: React.PropTypes.数据类型
}(2)限制数据类型,并必须传值
static propTypes={
属性: React.PropTypes.数据类型.isRequired
}(3)自定义验证规则
- props 是所有属性对象
- propName 是当前要验证的属性名
static propTypes = {
b: function(props, propName, componentName) {
if (props[propName]<20){
return new Error('值必须大于20')
}
}
}在class或函数外写
1.接收属性,并且设置默认值
class 组件名 extends React.Component
{
render() {
console.log(this.props) // {a:1, b:2}
return JSX
}
}
// 接收属性,并且设置默认值
组件名.defaultProps = {
a: false,
b: 2,
}
// 限制数据类型
组件名.propTypes = {
a: React.PropTypes.string.isRequired,
b: (props, propName, componentName)=>{
if (props[propName]<20){
return new Error('值必须大于20')
}
}
}
<组件名 a="1" b="2" />4.事件处理
1.语法
1.1 在render里定义
- 直接调用
onClick={fn}
render() {
const clickFun0 = function () {
alert(0);
}
return(
<button onClick={clickFun0}>登录0</button>
)
//...
}1.2 在render外定义
- 通过this调用
onClick={this.fn}
class Login extends React.Component {
clickFun1() {
alert(1);
}
render() {
return(
<button onClick={this.clickFun1}>登录1</button>
)
}
//...
}**注意:**调用函数不能加(),加了会立即执行函数并陷入死循环,这样打印this是undefined
原因:react数据更新就会渲染组件,调用函数加(),就会立即执行,执行就会重新渲染组件,渲染完毕后又会立即执行,从而陷入死循环
例子:
<script type="text/babel">
class Login extends React.Component {
clickFun1() {
alert(1);
}
clickFun2(e){
e.preventDefault();
}
render() {
const clickFun0 = function () {
alert(0);
}
return(<fieleset>
<legend>事件</legend>
<button onClick={clickFun0}>登录0</button>
<button onClick={this.clickFun1}>登录1</button>
<hr />
<form action="get" action="03.JSX.html">
<input type="text" />
<input type="submit" value="登录" onClick={this.clickFun2} />
</form>
</fieleset>)
}
}
ReactDOM.render(<Login />, document.querySelector("#root"));
</script>2.解决this指向问题
clickFn1(){
console.log(this)
}
render(){
return(
<button onClick={this.clickFn1}>更新</button>
)
}直接调用函数打印的this是undifined
1.箭头函数
//...
clickFn1=()=>{
console.log(this)
}
render(){
return(
<button onClick={this.clickFn1}>更新</button>
)
}
//...上述办法中,给类组件定义了普通属性,这样会导致不必要的内存占用
开发中更多使用以下方法
//...
clickFn1(){
console.log(this)
}
render(){
return(
// 注意:调用的时候加了()
<button onClick={ ()=>this.clickFn1() }>更新</button>
)
}
//...2.使用bind()
//...
clickFn1(){
console.log(this)
}
render(){
return(
// 注意:调用的时候加了()
<button onClick={ this.clickFn1.bind(this) }>更新</button>
)
}
//...3.事件传参
1.箭头函数
clickFn(e,num){
console.log(e) // 事件对象
console.log(num) // 111
}
//...
render(){
return (
<div>
<button onClick={ e=>this.clickFn(e,111) }>更新1</button>
</div>
)
}2.bind(this)
通过this.fn.bind(this)实现传参
- 传参时,第一个参数是
this,也就是组件实例,第二个以后都是函数的实参 - 定义函数时,最后一个参数是事件对象,前面依次是形参
clickFn(a,b,c,d,e){
console.log(a) // 11
console.log(b) // 22
console.log(c) // 33
console.log(d) // 44
console.log(e) // 事件对象
}
//...
render(){
return (
<div>
<button onClick={ this.clickFn.bind(this,11,22,33,44) }>更新1</button>
</div>
)
}5.ref标识
作用:可以获取dom元素
1.字符串形式的ref
- 已弃用
- 通过在标签中添加
ref属性
添加标识
//...
render(){
return(
<input ref="标识"/>
)
}
//...获取
this.refs.标识2.createRef
创建内存空间
在constructor中通过this.键 = React.createRef()
class Test extends React.Component{
//创建内存空间
const xxRef= React.createRef()
render(){
return (
//添加标识
<input ref={this.xxRef}/>
)
}
}获取
this.xxRef.current6.生命周期
6.1 挂载
| 生命周期 | 触发机制 | 场景 |
|---|---|---|
| componentWillMount() | 首次render渲染前 | 16.3弃用 |
| render() | 首次、后续更新 | 组件渲染 |
| componentDidMount() | 首次render渲染后 | 异步请求、初始化echarts、swiper等 |
| componentWillUnmount () | 所在组件卸载 | 清除非react资源,登陆定时器、定时器特效等 |
6.2 更新
| 生命周期 | 触发机制 | 场景 |
|---|---|---|
| componentWillReceiveProps() | 属性更新 | 16.3弃用 |
| shouldComponentUpdate() | 属性或状态更新 | 性能优化阻止组件无效渲染 |
| componentWillUpdate() | 所在组件属性/状态更新前 | 16.3弃用 |
| componentDidUpdate() | 所在组件属性/状态更新后 | 聊天、监控父传递的数据、数据改变跟新DOM等 |
性能优化:
shouldComponentUpdate(nextProps, nextState)
作用:控制组件是否更新的阀门,性能优化阻止组件无效渲染
- nextProps:更新后的新属性
- nextState:更新后的新状态
- 返回值为布尔值,默认为
true。若返回false,则后续流程不会进行。
注意:单纯的返回true或false不能准确的更新组件,我们需要先判断再确定是否更新
解决办法
1.组件继承React.PureComponent (推荐)
class Son extents React.PureComponent2.自己写循环挨个值判断
shouldComponentUpdate(nextProps, nextState){
for(key in this.state){
if(this.state[key]!==nextState){
return true
break
}
}
return false
}3.使用JSON对象提供的方法
shouldComponentUpdate(nextProps, nextState){
return JSON.stringify(this.state) != JSON.stringify(nextState)
}4.使用第三方库 lodash
shouldComponentUpdate(nextProps, nextState){
return !_.isEqual(this.state, nextState)
}6.3 卸载
| 生命周期 | 触发机制 | 场景 |
|---|---|---|
| componentWillUnmount () | 所在组件卸载 | 清除非react资源,登陆定时器、定时器特效等 |
6.4 版本16.3弃用
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
6.5 版本16.3新增
- static getDerivedStateFromProps 监控props变化更新state用
- getSnapshotBeforeUpdate 在更新DOM之前收集DOM信息
7.受控组件&非受控组件
包含表单的组件分类:
1.受控组件:表单数据由react控制
- 当表单的value属性绑定模型中的数据时,表单的数据只能通过onChange事件改变
2.非受控组件:表单数据非react控制,而是自己写dom操作,现用现取。即需要使用时,再获取节点得到数据
class Login extends React.Component {
state = {
val: "",
};
handleChange = (e) => {
this.setState({
val: e.target.value,
});
};
handleChange2 = () => {
const title = document.querySelector(".unCon").value;
console.log(title)
};
render() {
return (
<div>
受控input:<input value={this.state.val} onChange={(e) => this.handleChange(e)} />
<hr/>
非受控input:<input className="unCon" onChange={() => this.handleChange2()} />
</div>
);
}
}8.Context
作用:
- Context提供了一个在多级组件中共享数据的方式,而不是通过组件树逐层传递
语法:
1.创建context对象
const {Provider, Consumer} = React.createContext()2.传递数据
Provider包裹根组件 并通过value属性传递数据对象(注:必须是value属性)
下述代码传递了一个对象,并有a和b两个属性
ReactDOM.render(
<Provider value={{ a: 1, b: 2 }}>
<Father />
</Provider>,
document.querySelector("#app")
);3.接收使用数据
- 在需要使用数据的组件中通过语法
<Consumer></Consumer>包起来 就可以使用
// 子组件中
class Son extends React.Component {
render() {
return (
<fieldset>
<legend>子组件</legend>
<Consumer>
{(value) => (
<div>
<p>a:{value.a}</p>
<p>b:{value.b}</p>
</div>
)}
</Consumer>
<SonSon/>
</fieldset>
);
}
}
// 孙组件中
class SonSon extends React.Component {
render() {
return (
<fieldset>
<legend>孙组件</legend>
<Consumer>
{(value) => (
<div>
<p>a:{value.a}</p>
<p>b:{value.b}</p>
</div>
)}
</Consumer>
</fieldset>
);
}
}9.children
作用:
可以获取父双标签里面的数据并解析超文本
相当于vue的插槽
语法
父组件调用子组件时,在双标签中写数据或超文本
class Father extends React.Component{
render(){
return(
<Son>
<h1>父组件传递超文本</h1>
</Son>
)
}
}子组件通过this.props.children接收父组件传递的数据或超文本
class Son extends React.Component{
render(){
return(
<div>{this.props.children}</div>
)
}
}