React 笔记 3

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>
                &nbsp;&nbsp;&nbsp;&nbsp;
                <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>
                &nbsp;&nbsp;&nbsp;&nbsp;
                <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>
                &nbsp;&nbsp;&nbsp;&nbsp;
                <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组件中

  1. 基于数据驱动(修改状态数据,REACT帮助我们重新渲染视图)完成的组件叫做“受控组件(受数据控制的组件)”

  2. 基于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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值