Skip to content

二、class组件

1.语法

在class里的render()函数内返回dom结构

jsx
<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

jsx
<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.获取

jsx
<h1>{this.state.键}</h1>

//可以使用解构赋值
const {} =this.state
<h1>{键}</h1>

4.修改

**注意:**setState是异步的

  • callback可选,在值被修改后触发
jsx
// 写法一
this.setState({
    键1:值1,
    // ...
} [, callback])

// 写法二
// oldState为旧数据
this.setState((oldState)=>{
    return{
        键:值
    }
}[,callback])

3.属性props

1.概念

父组件调用子组件时,给子组件添加的属性会在子组件的props上

2.语法

切记切记props只读不能修改

jsx
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.接收属性,并且设置默认值

jsx
class 组件名 extends React.Component
{  
    static defaultProps = {
        a: false,
        b: 2,
    }
    //...
}

2.给传过来的属性添加验证规则

(1)限制数据类型

jsx
static propTypes={
	属性: React.PropTypes.数据类型
}

(2)限制数据类型,并必须传值

jsx
static propTypes={
	属性: React.PropTypes.数据类型.isRequired
}

(3)自定义验证规则

  • props 是所有属性对象
  • propName 是当前要验证的属性名
jsx
static propTypes = {
    b: function(props, propName, componentName) {
        if (props[propName]<20){
            return new Error('值必须大于20')
        }
    }
}

在class或函数外写

1.接收属性,并且设置默认值

jsx
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}
jsx
render() {
    const clickFun0 = function () {
        alert(0);
    }
    return(
        <button onClick={clickFun0}>登录0</button>
    )
    //...
}

1.2 在render外定义

  • 通过this调用onClick={this.fn}
jsx
class Login extends React.Component {
    clickFun1() {
        alert(1);
    }

    render() {
        return( 
            <button onClick={this.clickFun1}>登录1</button>
        )
    }
    //...
}

**注意:**调用函数不能加(),加了会立即执行函数并陷入死循环,这样打印thisundefined

原因:react数据更新就会渲染组件,调用函数加(),就会立即执行,执行就会重新渲染组件,渲染完毕后又会立即执行,从而陷入死循环

例子

jsx
<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指向问题

jsx
clickFn1(){
    console.log(this)
}
render(){
    return(
        <button onClick={this.clickFn1}>更新</button>
    )
}

直接调用函数打印的this是undifined

1.箭头函数

jsx
//...
clickFn1=()=>{
    console.log(this)
}
render(){
    return(
        <button onClick={this.clickFn1}>更新</button>
    )
}
//...

上述办法中,给类组件定义了普通属性,这样会导致不必要的内存占用

开发中更多使用以下方法

jsx
//...
clickFn1(){
    console.log(this)
}
render(){
    return(
        // 注意:调用的时候加了()
        <button onClick={ ()=>this.clickFn1() }>更新</button>
    )
}
//...

2.使用bind()

jsx
//...
clickFn1(){
    console.log(this)
}
render(){
    return(
        // 注意:调用的时候加了()
        <button onClick={ this.clickFn1.bind(this) }>更新</button>  
    )
}
//...

3.事件传参

1.箭头函数

jsx
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,也就是组件实例,第二个以后都是函数的实参
  • 定义函数时,最后一个参数是事件对象,前面依次是形参
jsx
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属性

添加标识

jsx
//...
render(){
    return(
    	<input ref="标识"/>
    )
}
//...

获取

jsx
this.refs.标识

2.createRef

创建内存空间

constructor中通过this.键 = React.createRef()

jsx
class Test extends React.Component{
    //创建内存空间
    const xxRef= React.createRef()
    render(){
        return (
            //添加标识
        	<input ref={this.xxRef}/>
        )
    }
}

获取

jsx
this.xxRef.current

6.生命周期

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 (推荐)

jsx
class Son extents React.PureComponent

2.自己写循环挨个值判断

jsx
shouldComponentUpdate(nextProps, nextState){
	for(key in this.state){
		if(this.state[key]!==nextState){
			return true
			break
		}
	}
    return false
}

3.使用JSON对象提供的方法

js
shouldComponentUpdate(nextProps, nextState){
	return JSON.stringify(this.state) != JSON.stringify(nextState)
}

4.使用第三方库 lodash

jsx
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操作,现用现取。即需要使用时,再获取节点得到数据

jsx
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对象

jsx
 const {Provider, Consumer} = React.createContext()

2.传递数据

  • Provider包裹根组件 并通过value属性传递数据对象(注:必须是value属性

  • 下述代码传递了一个对象,并有a和b两个属性

jsx
ReactDOM.render(
    <Provider value={{ a: 1, b: 2 }}>
        <Father />
    </Provider>,
    document.querySelector("#app")
);

3.接收使用数据

  • 在需要使用数据的组件中通过语法 <Consumer></Consumer>包起来 就可以使用
jsx
// 子组件中
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的插槽

语法

父组件调用子组件时,在双标签中写数据或超文本

jsx
class Father extends React.Component{
    render(){
        return(
        	<Son>
                <h1>父组件传递超文本</h1>
            </Son>
        )
    }
}

子组件通过this.props.children接收父组件传递的数据或超文本

jsx
class Son extends React.Component{
    render(){
        return(
        	<div>{this.props.children}</div>
        )
    }
}

MIT Licensed