import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import PropTypes from 'prop-types';
class Vote extends React.Component {
//=>组件传递的属性是只读的,我们为其设置默认值和相关规则
static defaultProps = {};
static propTypes = {
title: PropTypes.string.isRequired
};
constructor(props) {
super(props);
//=>INIT STATE
this.state = {
n: 0,//=>支持人数
m: 0 //=>反对人数
};
}
render() {
let {n, m} = this.state,
rate = (n + m) === 0 ? '0%' : ((n / (n + m) * 100).toFixed(2) + '%');
return <section className='panel panel-default' style={{width: '60%', margin: '20px auto'}}>
<div className='panel-heading'>
<h3 className='panel-title'>{this.props.title}</h3>
</div>
<div className='panel-body'>
支持人数:{n}
<br/><br/>
反对人数:{m}
<br/><br/>
支持率:{rate}
</div>
<div className='panel-footer'>
<button className='btn btn-success'
onClick={this.support}>支持
</button>
<button className='btn btn-danger'
onClick={this.against}>反对
</button>
</div>
</section>;
}
//=>投票:支持&&反对
support = ev => this.setState({n: this.state.n + 1});
against = ev => this.setState({m: this.state.m + 1});
}
ReactDOM.render(<main>
<Vote title='世界杯小组赛法国VS秘鲁,法国队必胜!'/>
<Vote title='世界杯小组赛阿根廷VS克罗地亚,壮哉我大梅西!'/>
</main>, root);
JSX中的事件绑定:
render(){
return <button className='btn btn-success'
onClick={this.support}>
支持</button>;
}
support(ev){
//=>THIS:undefined(不是我们理解的当前操作的元素)
//=>ev.target:通过事件源可以获取当前操作的元素(一般很少操作,因为框架主要是数据驱动所有DOM的改变)
}
如果能让方法中的THIS变成当前类的实例就好了,这样可以操作属性和状态等信息
render(){
//=>THIS:实例
return <button className='btn btn-success'
onClick={this.support.bind(this)}>
支持</button>;
}
support(ev){
//=>THIS:实例
}
render(){
//=>THIS:实例
return <button className='btn btn-success'
onClick={this.support}>
支持</button>;
}
support=ev=>{
//=>THIS:继承上下文中的THIS(实例), 真实项目中,给JSX元素绑定的事件方法一般都是箭头函数,目的是为了保证函数中的THIS还是实例
}
* REFS:是REACT中专门提供通过操作DOM来实现需求的方式,它是一个对象,存储了当前组件中所有设置REF属性的元素(元素REF属性值是啥,REFS中存储的元素的属性名就是啥)
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import PropTypes from 'prop-types';
class Vote extends React.Component {
//=>组件传递的属性是只读的,我们为其设置默认值和相关规则
static defaultProps = {};
static propTypes = {
title: PropTypes.string.isRequired
};
constructor(props) {
super(props);
}
render() {
return <section className='panel panel-default' style={{width: '60%', margin: '20px auto'}}>
<div className='panel-heading'>
<h3 className='panel-title'>{this.props.title}</h3>
</div>
<div className='panel-body'>
支持人数:<span ref='spanLeft'>0</span>
<br/><br/>
反对人数:<span ref='spanRight'>0</span>
<br/><br/>
支持率:<span ref='spanRate'>0</span>
</div>
<div className='panel-footer'>
<button className='btn btn-success'
onClick={this.support}>支持
</button>
<button className='btn btn-danger'
onClick={this.against}>反对
</button>
</div>
</section>;
}
//=>投票:支持&&反对
support = ev => {
/!*
* REFS:是REACT中专门提供通过操作DOM来实现需求的方式,它是一个对象,存储了当前组件中所有设置REF属性的元素(元素REF属性值是啥,REFS中存储的元素的属性名就是啥)
* {
* spanLeft:span
* ...
* }
*!/
let {spanLeft} = this.refs;
spanLeft.innerHTML++;
this.computed();
};
against = ev => {
let {spanRight} = this.refs;
spanRight.innerHTML++;
this.computed();
};
computed = () => {
let {spanLeft, spanRight, spanRate} = this.refs,
n = parseFloat(spanLeft.innerHTML),
m = parseFloat(spanRight.innerHTML),
rate = (n + m) === 0 ? '0%' : ((n / (n + m) * 100).toFixed(2) + '%');
spanRate.innerHTML = rate;
};
}
{/*
* ref='spanLeft'
* 是在当前实例上挂载一个属性refs(对象),存储所有的ref元素
*
* x => this.spanLeft = x
* x代表当前元素,它的意思是,把当前元素直接挂载到实例上,后期需要用到元素,直接this.spanLeft获取即可
*/}
class Vote extends React.Component {
//=>组件传递的属性是只读的,我们为其设置默认值和相关规则
static defaultProps = {};
static propTypes = {
title: PropTypes.string.isRequired
};
constructor(props) {
super(props);
}
render() {
return <section className='panel panel-default' style={{width: '60%', margin: '20px auto'}}>
<div className='panel-heading'>
<h3 className='panel-title'>{this.props.title}</h3>
</div>
<div className='panel-body'>
{/*
* ref='spanLeft'
* 是在当前实例上挂载一个属性refs(对象),存储所有的ref元素
*
* x => this.spanLeft = x
* x代表当前元素,它的意思是,把当前元素直接挂载到实例上,后期需要用到元素,直接this.spanLeft获取即可
*/}
支持人数:<span ref={x => this.spanLeft = x}>0</span>
<br/><br/>
反对人数:<span ref={x => this.spanRight = x}>0</span>
<br/><br/>
支持率:<span ref={x => this.spanRate = x}>0%</span>
</div>
<div className='panel-footer'>
<button className='btn btn-success'
onClick={this.support}>支持
</button>
<button className='btn btn-danger'
onClick={this.against}>反对
</button>
</div>
</section>;
}
//=>投票:支持&&反对
support = ev => {
let {spanLeft} = this;
spanLeft.innerHTML++;
this.computed();
};
against = ev => {
let {spanRight} = this;
spanRight.innerHTML++;
this.computed();
};
computed = () => {
let {spanLeft, spanRight, spanRate} = this,
n = parseFloat(spanLeft.innerHTML),
m = parseFloat(spanRight.innerHTML),
rate = (n + m) === 0 ? '0%' : ((n / (n + m) * 100).toFixed(2) + '%');
spanRate.innerHTML = rate;
};
}
ReactDOM.render(<main>
<Vote title='世界杯小组赛法国VS秘鲁,法国队必胜!'/>
<Vote title='世界杯小组赛阿根廷VS克罗地亚,壮哉我大梅西!'/>
</main>, root);
在REACT组件中
基于数据驱动(修改状态数据,REACT帮助我们重新渲染视图)完成的组件叫做“受控组件(受数据控制的组件)”
基于REF操作DOM实现视图更新的,叫做“非受控组件” =>真实项目中,建议大家多使用“受控组件”
基于表单元素的ONCHANGE实现MVVM双向绑定
VUE:[MVVM] 数据更改视图跟着更改,视图更改数据也跟着更改(双向数据绑定) REACT:[MVC] 数据更改视图跟着更改(原本是单向数据绑定,但是我们可以自己构建出双向的效果)
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import PropTypes from 'prop-types';
class Temp extends React.Component {
constructor() {
super();
this.state = {
text: '周啸天'
};
}
componentDidMount() {
setTimeout(() => {
this.setState({text: '珠峰培训'});
}, 1000);
}
render() {
let {text} = this.state;
return <section className='panel panel-default'>
<div className='panel-heading'>
<input type="text" className='form-control'
value={text}
onChange={ev => {
//=>在文本框的ON-CHANE中修改状态信息:实现的是视图改变数据
this.setState({
text: ev.target.value
});
}}/>
</div>
<div className='panel-body'>
{text}
</div>
</section>;
}
}
ReactDOM.render(<Temp/>, root);
REACT组件的生命周期函数
生命周期函数(钩子函数):
描述一个组件或者程序从创建到销毁的过程,我们可以在过程中间基于钩子函数完成一些自己的操作(例如:在第一次渲染完成做什么,或者在二次即将重新渲染之前做什么等...)
[基本流程]
constructor 创建一个组件
componentWillMount 第一次渲染之前
render 第一次渲染
componentDidMount 第一次渲染之后
[修改流程:当组件的状态数据发生改变(SET-STATE)或者传递给组件的属性发生改变(重新调用组件传递不同的属性)都会引发RENDER重新执行渲染(渲染也是差异渲染)] shouldComponentUpdate 是否允许组件重新渲染(允许则执行后面函数,不允许直接结束即可)
componentWillUpdate 重现渲染之前
render 第二次及以后重新渲染
componentDidUpdate 重现渲染之后
componentWillReceiveProps:父组件把传递给子组件的属性发生改变后触发的钩子函数
[卸载:原有渲染的内容是不消失的,只不过以后不能基于数据改变视图了] componentWillUnmount:卸载组件之前(一般不用)
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import PropTypes from 'prop-types';
/*class A extends React.Component {
//=>这个是第一个执行的,执行完成后(给属性设置默认值后)才向下执行
// static defaultProps = {};
constructor() {
super();
console.log('1=CONSTRUCTOR');
this.state = {n: 1};
}
componentWillMount() {
console.log('2=WILL-MOUNT:第一次渲染之前', this.refs.HH);//=>UNFRFINED
}
componentDidMount() {
console.log('4=DID-MOUNT:第一次渲染之后', this.refs.HH);//=>DIV
/!*
* 真实项目中在这个阶段一般做如下处理:
* 1. 控制状态信息更改的操作
* 2. 从服务器获取数据,然后修改状态信息,完成数据绑定
* ...
*!/
setInterval(() => {
this.setState({n: this.state.n + 1});
}, 5000);
}
shouldComponentUpdate(nextProps, nextState) {
/!*
* 在这个钩子函数中,我们获取的STATE不是最新修改的,而是上一次的STATE值
* 例如:第一次加载完成,5000MS后,我们基于SET-STATE把N修改为2,但是此处获取的还是1呢
*
* 但是这个方法有两个参数
* nextProps:最新修改的属性信息
* nextState:最新修改的状态信息
*!/
console.log('5=是否允许更新,函数返回TRUE就是允许,返回FALSE就是不允许');
if (nextState.n > 3) {
return false;
}
return true;
}
componentWillUpdate(nextProps, nextState) {
//=>这里获取的状态是更新之前的(和SHOULD相同也有两个参数存储最新的信息)
console.log('6=组件更新之前', this.state.n, nextState);
}
componentDidUpdate() {
//=>这里获取的状态是更新之后的
console.log('8=组件更新之后', this.state.n);
}
render() {
console.log('RENDER');
return <div ref='HH'>
{this.state.n}
</div>;
}
}*/
/*function queryData() {
return new Promise(resolve => {
setTimeout(() => {
resolve(2);
}, 3000);
});
}
class A extends React.Component {
constructor() {
super();
console.log('1=CONSTRUCTOR');
this.state = {n: 1};
}
componentWillMount() {
console.log('2=WILL-MOUNT:第一次渲染之前');
//=>在WILL_MOUNT中,如果直接的SET-STATE修改数据,会把状态信息改变后,然后RENDER和DID_MOUNT;但是如果SET-STATE是放到一个异步操作中完成(例如:定时器或者从服务器获取数据),也是先执行RENDER和DID,然后在执行这个异步操作修改状态,紧接着走修改的流程(这样和放到DID_MOUNT中没啥区别的),所以我们一般把数据请求放到DID中处理。
//=>真实项目中的数据绑定,一般第一次组件渲染,我们都是绑定的默认数据,第二次才是绑定的从服务器获取的数据(有些需求我们需要根据数据是否存在判断显示隐藏等)
this.setState({
n: 2
});
console.log(this.state.n);
}
/!*async componentWillMount() {
console.log('2=WILL-MOUNT:第一次渲染之前');
let result = await queryData();
this.setState({
n: result
});
}*!/
componentDidMount() {
console.log('4=DID-MOUNT:第一次渲染之后', this.refs.HH);
}
render() {
console.log('3=RENDER');
return <div ref='HH'>
{this.state.n}
</div>;
}
}*/
class A extends React.Component {
constructor() {
super();
console.log('1=CONSTRUCTOR');
}
componentWillMount() {
console.log('2=WILL-MOUNT:第一次渲染之前');
}
componentDidMount() {
console.log('4=DID-MOUNT:第一次渲染之后', this.refs.HH);
}
componentWillReceiveProps(nextProps, nextState) {
console.log('组件属性改变', this.props.n, nextProps.n);
//=>属性改变也会触发子组件重新渲染,继续完成修改这套流程
}
shouldComponentUpdate() {
console.log('SHOULD');
return true;
}
render() {
console.log('RENDER');
return <div ref='HH'>
{this.props.n}
</div>;
}
}
class B extends React.Component {
constructor() {
super();
this.state = {
n: 1
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
n: 2
});
}, 3000);
}
render() {
//=>复合组件:组件嵌套(大组件嵌套小组件)
return <div>
{/*把父组件的状态信息作为属性传递给子组件*/}
<A n={this.state.n}/>
</div>;
}
}
ReactDOM.render(<B/>, root);
复合组件之间的信息传递
父组件包裹子组件,包裹中的子组件可以使用父组件方法,外面定义的子组件使用prop继承
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import PropTypes from 'prop-types';
/*HEAD*/
class Head extends React.Component {
render() {
return <div className='panel-heading'>
<h3 className='panel-title'>
{/*子组件通过属性获取父组件传递的内容*/}
点击次数:{this.props.count}
</h3>
</div>;
}
}
/*BODY*/
class Body extends React.Component {
render() {
return <div className='panel-body'>
<button className='btn btn-success'
onClick={this.props.callBack}>点我啊!
</button>
</div>;
}
}
/*PANEL*/
class Panel extends React.Component {
constructor() {
super();
this.state = {n: 0};
}
fn = () => {
//=>修改PANEL的状态信息
this.setState({
n: this.state.n + 1
});
};
render() {
return <section className='panel panel-default' style={{width: '50%', margin: '20px auto'}}>
{/*父组件中在调取子组件的时候,把信息通过属性传递给子组件*/}
<Head count={this.state.n}/>
{/*父组件把自己的一个方法基于属性传递给子组件,目的是在子组件中执行这个方法*/}
<Body callBack={this.fn}/>
</section>;
}
}
ReactDOM.render(<Panel/>, root);