B站 React教程笔记day1(2)组件传值

 视频地址

4. React中的数据传递

4.1 组件自己身上的属性变换,不会引起视图改变(不推荐使用)

说明:在构造器函数中定义this.a=100表示给组件的实例绑定一个a属性,值为100。在jsx中使用的时候,直接this.a即可。

import React from "react";

class App extends React.Component{
  constructor() {
    super();
    this.a = 10000;
  }

  add() {
    this.a++;
    console.log(this.a);
  }
  render() {
    return (
      <div>
        <div>{this.a}</div>
        <div>
          <input type="button" value="按钮" onClick={(this.add).bind(this)} />
        </div>
      </div>
    )
  }
}

//向外暴露
export default App;

案例总结:

① 绑定监听使用onClick、onMousedown、onMouseenter、onBlur,把on后面的字母大写,React会自动识别React事件。

绑定监听函数的时候,this上下文是有问题的,所以需要使用bind()方法来设置上下文

③ 绑定监听函数的时候,注意用{}而不是""

所以React中,组件自己的属性的变化不会引发视图的变化。

4.2 闭包中的值变化,不会引起视图改变

import React from "react";

class App extends React.Component{
  render() {
    let num = 300
    return (
      <div>
        <div>{num}</div>
        <div>
          <input type="button" value="按钮" onClick={()=>{num++;console.log(num)}} />
        </div>
      </div>
    )
  }
}

//向外暴露
export default App;

4.3 三兄弟之一:state

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

import React from "react";

class App extends React.Component{
  constructor() {
    super()
    this.state = {
      a: 1,
      b: 10,
      c: 100
    }
  }

  add() {
    this.setState({a: this.state.a + 1})
  }
  render() {
    let num = 300
    return (
      <div>
        <h1>我是APP组件</h1>
        <h2>我有 state 状态机</h2>
        <h2>a: {this.state.a}</h2>
        <h2>b: {this.state.b}</h2>
        <h2>c: {this.state.c}</h2>
        <div>
          <input type="button" value="按钮" onClick={(this.add).bind(this)} />
        </div>
      </div>
    )
  }
}

//向外暴露
export default App;

只有更新三兄弟,才会引发Virtual DOM的改变,从而改变DOM。

  1. 定义state: 在构造函数中使用this.state属性即可

  2. 使用state :在JSX中{this.state.a}

  3. 改变state: this.setState({a : this.state.a + 1}); 不能写++,因为state属性值只读。

state是内部的(所以也叫作local state),只有组件自己能改变自己的state,别人想改变自己的state,都不可能!

后面的Redux架构,所有组件自己的state越来越少用了,而是变为了Redux中的state,不要混淆。

4.4 三兄弟之二:props

定义在自定义组件标签上面的值,就是props

当 props 改变的时候,会引发Virtual DOM的改变,从而引发视图的重绘。react崇尚数据的单向流动,所以设计的时候就是让数据从父组件流向子组件props在子组件中是只读的,不能修改的

父组件向子组件传值:

 如果父组件App,往子组件MyCompo中传值,使用属性绑定

app/App.js:

import React from "react";
import MyCompo from './MyCompo.js'
class App extends React.Component{
  constructor() {
    super()
  }

  render() {
    return (
      <div>
        <h1>我是APP组件</h1>
        <div>
          <MyCompo a={100} b="1000" c={10000}></MyCompo>
        </div>
      </div>
    )
  }
}

//向外暴露
export default App;

子组件MyCompo中就可以无脑使用this.props来枚举传入的属性:

app/MyCompo.js:

import React from 'react';
import { PropTypes } from "prop-types";

class MyCompo extends React.Component {
  // 如果需要在构造函数中使用这个值,此时系统会将props最为构造函数的第一个参数传入:
  constructor(props) {
    super();
    // 拿 state 接收一下,就可以改变了
    this.state = {
      c: props.c
    }
  }

  render() {
    return (
      <div>
        <h1>我是 MyCompo 组件</h1>
        <p>{this.props.a}</p>
        <p>{this.props.b}</p>
        <p>{this.props.c}</p>
        <p>{this.state.c}</p>
        <p>
          <input type="button" value="按我" onClick={() => { this.setState({ c: this.state.c + 1 }); }} />
        </p>
      </div>
    )
  }
}

//定义组件传入的参数类型
//类名.propTypes,值是一个JSON。key就是需要传进来的props属性名,v就是对它的限制
MyCompo.propTypes = {
  a: PropTypes.string.isRequired,  // a属性是一个字符串,必传
  b: PropTypes.string,  // b属性是一个字符串,不必传
  c: PropTypes.number.isRequired // c属性是一个数,必传
};


export default MyCompo;

如果需要在构造函数中使用这个值,此时系统会将props最为构造函数的第一个参数传入 

在子组件中,props是只读的,不能修改props的值。如果要修改,用state来接收。

props属性可以被验证有效性:

npm install --save-dev prop-types

但是现在,我想子组件父组件传值呢?就是父组件传递一个函数给子组件,子组件通过传参数调用函数将数据返回给父组件的函数,父组件的函数接受实参改变父组件中的state等值。

./App.js

import React, { Component } from "react";
import MyCompo from './Mycompo'

class App extends Component {
  constructor() {
    super()

    this.state = {
      d: 600
    }
  }

  setD(number) {
    this.setState({"d": number})
  }
  render() {
    return (
      <div>
        <p>我是App 组件,我有一个d状态: {this.state.d}</p>
        {/* 传递setD方法 和 d 的值 */}
        <MyCompo setD={(this.setD).bind(this)} d={this.state.d}></MyCompo>
      </div>
    );
  }
}

//向外暴露
export default App;

./MyCopmo.js

import React from "react"

class MyCompo extends React.Component {
  constructor(props) {
    super()

    this.state = {
      d: props.d
    }

    this.add = () => {
      // 设置子组件 d 的值
      this.setState({ d: this.state.d + 9 })
      // 调用父组件的setD函数 
      props.setD(this.state.d + 9)
    }
  }

  render() {
    return (
      <div>
        <hr />
        我是 M有Compo 组建
        <p>d: {this.state.d}</p>
        <p>
          <input type="button" value="按我更改d的值" onClick={this.add} />
        </p>
      </div>
    )
  }
}

//向外暴露
export default MyCompo;

数据的单向传递是React的精髓。

4.4 三兄弟之三:context

上下文的精髓是可以跨级传递数据的,爷爷组件可以直接传递数据到孙子组件:

Yeye.js:

import React from "react";
import Baba from "./Baba.js";
import PropTypes from "prop-types";

class Yeye extends React.Component{
    constructor(){
        super();
        this.state = {
            a : 100
        }
    }

    render(){
        return (
            <div>
                <h1>爷爷</h1>
                <Baba></Baba>
            </div>
        );
    }

    //得到孩子上下文,实际上这里表示一种设置,返回一个对象,这个对象就是现在这个家族体系共享的上下文。将上下文中的a值变为自己的状态中的a值
    getChildContext(){
        return {
            a : this.state.a
        }
    }
}

//设置child的上下文类型
Yeye.childContextTypes = {
    a : PropTypes.number.isRequired
}

export default Yeye;

Baba.js文件,没有写什么东西:

import React from "react";
import Sunzi from "./Sunzi";
import PropTypes from "prop-types";

class Baba extends React.Component{
    render(){
        return (
            <div>
                <h1>爸爸</h1>
                <Sunzi></Sunzi>
            </div>
        );
    }
}

export default Baba;

Sunzi.js

React 会将上下文当做构造函数的第二个参数传入:

import React from "react";
import PropTypes from "prop-types";

class Sunzi extends React.Component{
    constructor(props,context){
        super();
        console.log(context);  //得到上下文
    }

    render(){
        return (
            <div>
                <h1>孙子</h1>
            </div>
        );
    }
}

//设置上下文的类型
Sunzi.contextTypes = {
    a : PropTypes.number
}

export default Sunzi;

结论:

① 当祖先元素中更改了上下文的数据,此时所有的子孙元素中的数据都会更改,视图也会更新;

② 反之不成立,可以认为上下文的数据在子孙元素中是只读的。此时又要需要使用奇淫技巧,就是在context中共享一个操作祖先元素的函数,子孙元素通过上下文获得这个函数,从而操作祖先元素的值。

也就是说,state是自治的不涉及传值的事儿;props是单向的,父亲→儿子;context也是单向的,祖先→后代。如果要反向,就要传入一个函数。

Yeye.js:

import React from "react";
import Baba from "./Baba.js";
import PropTypes from "prop-types";

class Yeye extends React.Component{
    constructor(){
        super();
        this.state = {
            a : 100
        }
    }

    //设置自己的A值,这个函数要进入上下文的“通道”中
    addA(){
        this.setState({a : this.state.a + 1});
    }

    render(){
        return (
            <div>
                <h1>爷爷{this.state.a} /></h1>
                <Baba></Baba>
            </div>
        );
    }

    //得到孩子上下文,实际上这里表示一种设置
    getChildContext(){
        return {
            a : this.state.a,
            addA : (this.addA).bind(this)
        }
    }
}

Yeye.childContextTypes = {
    a         : PropTypes.number.isRequired,
    addA     : PropTypes.func.isRequired        //func表示函数
}

export default Yeye;

Sunzi.js:

import React from "react";
import PropTypes from "prop-types";

class Sunzi extends React.Component{
    constructor(props,context){
        super();
    }

    render(){
        return (
            <div>
                <h1>孙子{this.context.a} <input type="button" value="按我" onClick={this.context.addA}/></h1>
            </div>
        );
    }
}

Sunzi.contextTypes = {
    a         : PropTypes.number,
    addA     : PropTypes.func
}

export default Sunzi;

context很少用,传值基本用props。除非特别深的跨级别传值,可以用context。

猜你喜欢

转载自www.cnblogs.com/houfee/p/10845334.html