React-class ——React 组件、 React Props(属性传值)、React State(状态)

目录

一、React 组件

1、定义单个组件

(1)定义组件

(2)使用组件

 2、定义复合组件

二、 React Props

1、定义和使用props 传值

- 通过React**类定义**组件时:

通过React**函数定义** 组件时:

2、默认Props

3、多属性传值

三、React State

 1、如何定义State

2、setState设置状态

3、State 与 Props 区别

4、state/props 实现父子组件通信


一、React 组件

组件一共有两种:有状态和无状态

React.js 中一切皆组件,用 React.js 写的其实就是 React.js 组件。

我们在编写 React.js 组件的时候,一般都需要继承 React.js 的 Component(类定义)。一个组件类必须要实现一个 render 方法,这个 render 方法必须要返回一个 JSX 元素。

但这里要注意的是,必须要用一个外层的 JSX 元素把所有内容包裹起来。(有一个根元素)

返回并列多个 JSX 元素是不合法的。

1、定义单个组件

(1)定义组件

方式1:通过React自定义组件(DOM元素):**类定义**

import React, {Component} from 'react';
class MyApp extends Component {
}

import React from 'react';
class MyApp extends React.Component {
    constructor(props){
        super(props);
        this.msg="hello,react"
  	}
    
	render() {
		return (
			<div>
                <div>{this.msg}</div>
				<h2>这是标题2</h2>
				<h3>这是标题2</h3>
			</div>
		);
	}
}
export default MyApp;

方式2:通过React自定义组件(DOM元素):**函数定义**

import React from 'react';
//箭头函数
const MyApp = () => <div><p>这是一个段落</p></div>;
export default MyApp;


//普通函数
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
export default Welcome;

方式3:**不建议使用**,React.createClass创建组件的方式在react 16版本中去除。

var MyApp = React.createClass({
  render: function() {
    return <h1>Hello World!</h1>;
  }
});

(2)使用组件

import React from 'react';
import ReactDOM from 'react-dom';
import MyApp from './js/MyApp.js'; //导入自定义组件

ReactDOM.render(<MyApp />, document.getElementById('root'));

 2、定义复合组件

我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离

import React from 'react';
import ReactDOM from 'react-dom';

class WebSite extends React.Component {
	render() {
		return (
      		<div>
        		<Name name={this.props.name} />
        		<Link site={this.props.site} />
      		</div>
    	);
	}
}
//局部组件Name
class Name extends React.Component {
	render() {
		return (
      		<h1>{this.props.name}</h1>
    	);
	}
}
//局部组件Link
class Link extends React.Component {
	render() {
		return (
      		<h1>{this.props.site}</h1>
    	);
	}
}

ReactDOM.render(
	<WebSite name="百度一下,你就知道" site='http://www.baidu.com' />, 
	document.getElementById('root')
)

二、 React Props

React 的一大特点是  单向数据流 。

React 中的每一个组件,都包含有一个属性(props),**属性主要是从父组件传递给子组件的**,在组件内部,我们可以通过this.props获取属性对象。

1、定义和使用props 传值

- 通过React**类定义**组件时:

- 在父组件render 方法中调用组件时使用**key/value** 的形式来指定属性。
  - 在自定义子组件中通过**this.props.key** 来获得组件属性的值,需要使用{}括起来。

(类可以有props属性,是因为类继承了官方react的组件,函数就没有props属性)

// 类定义组件时,使用属性 this.props.属性名称
class MyApp extends React.Component {
	render() {
		return (<p>{this.props.name}</p>);
	}
}

ReactDOM.render(
	<MyApp name="张三" />, 
	document.getElementById('root')
);

通过React**函数定义** 组件时:

- 在父组件render 方法中调用组件时使用**key/value** 的形式来指定属性。
- 在自定义子组件中通过函数接收参数**props**,**props.key**来获得组件属性的值,需要使用{}括起来

// 函数定义组件时,在组件内部使用属性值:props.属性名称
function Welcome(props) { // 函数需要传递一个参数props
    return(<h2>{props.title}</h2>)
}

ReactDOM.render(
    <Welcome title="张三" />, 
    document.getElementById('root')
);

2、默认Props

定义默认props :

class MyApp extends React.Component {
	render() {
		return <p>this is my {this.props.name}</p>
	}	
}
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。
MyApp.defaultProps = {
	name: 'xxx'
};
	

ReactDOM.render(
	<MyApp />,
	document.getElementById('root')
);

3、多属性传值

(1)定义一个this.props对象,在对象中声明多个**键值对**,用于表示组件的属性

(2)在组件中使用**{...this.props}**的方式传递属性。

“...”表示JSX的延展操作符,这种方式可以很方便的为组件指定多个属性,并且为属性的值指定数据类型。

class MyApp extends React.Component {
	render() {
		return(
			<h1>{this.props.name} : {this.props.age} : {this.props.sex}</h1>
		);
	}
}




let p1 = {
	name: '张三', 
	age: 18,
	sex: '男'
};

ReactDOM.render(
    //<MyApp name='张三' age='18' sex='男' />
	<MyApp {...p1}/>,
	document.getElementById('root')
);

三、React State

React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。

 1、如何定义State

定义一个合适的State,是正确创建组件的第一步。

State必须能代表一个组件UI呈现的完整状态集,即组件的任何UI改变,都可以从State的变化中反映出来;

同时,State还必须是代表一个组件UI呈现的最小状态集,即State中的所有状态都是用于反映组件UI的变化,没有任何多余的状态,也不需要通过其他状态计算而来的中间状态。

组件中用到的一个变量是不是应该作为组件State,可以通过下面的4条依据进行判断:

1. 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。


2. 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。


3. 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。


4. 这个变量是否在组件的render方法中使用?如果**不是**,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性。

并不是组件中用到的所有变量都是组件的状态!

class LikeButton extends React.Component {
    //声明一个状态liked   使用this.state.liked获取状态的值
	constructor(props) {
        super(props)
        this.state={liked: false};
    }	
	handleClick(event) {
		this.setState({liked: !this.state.liked});
	}
	render() {
		return (
			<button onClick={this.handleClick}>
				你<strong>{this.state.liked ? '喜欢' : '不喜欢'}</strong>我,点我切换
			</button>
		)
	}
}

ReactDOM.render(
	<LikeButton />,
	document.getElementById('root')
)

注意:**React在ES6的实现中,规定state在constructor中实现**

正确定义State***的方式如下:

(1)在constructor中实现state

(2)在constructor中通过bind绑定事件函数(事件函数是用来改变状态)

(3)在事件函数内部使用setState函数更改状态

(4)在组件中的render函数中使用该状态

(5)在组件上需要设置监听事件,去触发事件函数的执行

//定义组件
class LikeButton extends React.Component {
    //constructor表示构造器,在constructor需要声明状态state,在声明state之前需要使用super(props);
	constructor(props) {
		super(props);//使用父类的属性
        
        //声明状态
		this.state = {
			liked: false
		}
		//Currently, you are calling bind. 
		//But bind returns a bound function. 
		//You need to set the function to its bound value.
		//目前,你正在调用绑定。但是绑定返回绑定函数。您需要将函数设置为其绑定值。
		this.handleClick = this.handleClick.bind(this);
	}
    
	handleClick(event) {
		this.setState({liked: !this.state.liked});
	}
    
	render() {
		return (
			<button onClick={this.handleClick}>
				你<strong>{this.state.liked ? '喜欢' : '不喜欢'}</strong>我,点我切换
			</button>
		)
	}
}

ReactDOM.render(
	<LikeButton />,
	document.getElementById('root')
)

2、setState设置状态

(1)语法: 

setState(object nextState[, function callback])

(2)说明:

- setState是React事件处理函数中和回调函数中触发UI更新的主要方法。


- 不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。


- setState()不一定是同步的,为了性能提升,React会批量执行state和DOM渲染。


- setState()总是会触发一次组件重绘,但可在shouldComponentUpdate()中实现一些条件渲染逻辑来解决。

setState默认是异步的,但有时候也是同步的

在计时器 和 原生Dom函数中,setState是一个同步函数,其它情况全是异步函数

class MyApp extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			clickCount: 0
		};

		this.handleClick = this.handleClick.bind(this);
	}

	handleClick(event) {
		this.setState({clickCount: this.state.clickCount + 1});
	}

	render() {
		return (
			<h2 onClick={this.handleClick}>
				点击后次数变更: {this.state.clickCount}
			</h2>
		);
	}
}

ReactDOM.render(
	<MyApp />,
	document.getElementById('root')
);

同步设置多个状态时(不常用),可以在setState函数的**第二个参数可以传递一个function回调函数,如下:

class MyApp extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			clickCount: 0, 
			isRed: false,
			smallFont: true
		};

		this.handleClick = this.handleClick.bind(this);
	}

	handleClick(event) {
		this.setState(
			{clickCount: this.state.clickCount + 1},
			function() {
				this.setState(
					{isRed: !this.state.isRed}, 
					function() {
						this.setState({smallFont: !this.state.smallFont});
					}
				);
			}
			
		);
		console.log(this.state.isred);
	}

	render() {
		var redStyle = {color: 'red', fontSize: 50};
		var blueStyle = {color: 'blue', fontSize: 14};
		return (
			<h2 onClick={this.handleClick} style={this.state.isRed ? redStyle : blueStyle}>
				点击后次数变更: {this.state.clickCount}
			</h2>
		);
	}
}

ReactDOM.render(
	<MyApp />,
	document.getElementById('root')
);

状态上移:

当存在多个组件共同依赖一个状态,或是当子组件的props 数据需要被修改时,将这个状态放到对应组件的父组件中:

//子组件
class Site extends React.Component {
	render() {
		return (
			<div>
				<button onClick={this.props.updateState}>点击改变</button>
				<h2>{this.props.myData}</h2>
			</div>
		);
	}
}

//父组件
class Content extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			value: 'hello'
		};
		this.handleChange = this.handleChange.bind(this);
	}

	handleChange(event) {
		this.setState({value: '你好'});
	}

	render() {
		return(
			<div>
				<Site myData={this.state.value} updateState={this.handleChange}/>
			</div>
		);
	}
}

3、State 与 Props 区别

除了State, 组件的Props也是和组件的UI有关的。他们之间的主要区别是:

  • props 中的数据都是外界传递过来的

  • state 中的数据都是组件私有的;(通过Ajax 获取回来的数据,一般都是私有数据)

  • props 中的数据都是只读的,不能重新赋值

  • state 中的数据都是可读可写的

  • State定义在constructor内部,在super(props)代码后面;Props默认值定义在类(组件)的外部

当子组件的属性值是可变值时,采用状态上移:

状态上移通过属性将父组件的状态传递到子组件,那么父组件的状态发生变化时,子组件的属性也会改变

4、state/props 实现父子组件通信

(自己实现反向传值,Vue中已经实现好了)

  • 子组件获取父组件整个组件进行传参

    • 父组件在调用子组件时,传入一整个组件给子组件<Children parent={ this } />

    • 父组件中定义一个方法getChildrenMsg(resulet, msg),用来获取子组件传来的值以及执行其他操作

    • 子组件在通过this.props来获取到一整个组件this.props.parent 或者this.props[parent]

    • 子组件调用父组件步骤2里定义的方法进行传值this.props.parent.getChildrenMsg(this,val)

Parent:

import Children from './Children'
export default class Parent extends Component {
    constructor(props) {
        super(props)
        this.state = {
            msg: '父组件传值给子组件',
            childrenMsg: ''
        }
    }

    getChildrenMsg = (result, msg) => {
        // console.log(result, msg)
        this.setState({
            childrenMsg: msg
        })
    }

    render() {
        return (
            <div>
                <h2>我是父组件 </h2>
                <h3>子组件传来的值为:{ this.state.childrenMsg }</h3>
                <hr/>
                <Children parent={ this } />
                {/*<Children parent={ this.getChildrenMsg.bind(this) } />*/}
            </div>
        )
    }
}

Children:

export default class Children extends Component {
    constructor(props) {
        super(props)
        this.state = {
            msg: '子组件传值给父组件'
        }
    }

    toParent = () => {
        // console.log(this.props.parent.getChildrenMsg.bind(this, this.state.msg))
        this.props.parent.getChildrenMsg(this, this.state.msg)
    }

    render() {
        return (
            <div>
                <h2>{ 我是子组件 }</h2>
                <button onClick={ this.toParent }>子组件传入给父组件</button>
            </div>
        )
    }
}

猜你喜欢

转载自blog.csdn.net/qq_52301431/article/details/127261842