React State && 事件处理 Event&& 组件通信

React 基础知识



State

State 状态

import React, { Component } from "react";

export default class StateCopm extends Component {
  /* 
    * 记住了:
      * state只能在当前组件中定义,也只能在当前组件中修改
      * 实例属性定义形式
      ! 构造函数定义形式 - 推荐
  */
  constructor(props) {
    super(props);
    this.state = {
      info: "李大傻蛋"
    };
  }
  // state = {
  //     name:'lishanyu'
  // }
  changeState = props => {
    /* 
    ! 1. 事件处理程序我们是直接定义为实例方法
    ! 2. 事件处理程序我们建议写成箭头函数 - this指向不改变
  */ // * 点击按钮改变状态
    /* 
      ! 1. 事件处理程序我们是直接定义为实例方法
      ! 2. 事件处理程序我们建议写成箭头函数 - this指向不改变
    */
    /* 
      ? 修改状态
        ! 在React中状态的修改只能使用setState()
        ! setState() 的作用是用来改变状态 更新视图的
        ! setState是异步的
      * setState( obj | function , [ callback ] ) 参数是有两个的
    */
    //    this.setState({
    //        info:"的确是是傻蛋!!!"
    //    })

    this.setState(
      () => {
        console.log(1);
        return {
          info: "第一个参数是函数"
        };
      },
      () => {
        console.log("张浩雨: StateCopm", 2);
        // console.log("这里是第二个参数(函数))");
        document.querySelector(".div").style.background = "pink";
      }
    );
    console.log(3);

    //! 打印结果为  3  1  2
  };

  render() {
    return (
      <div>
        <button onClick={this.changeState}>++++</button>
        {/* <p>state 的状态  { this.state.name }</p> */}
        <p>state 的状态--constructor {this.state.info}</p>
        <div className="div">
          <h5>XSS的原理和分类</h5>
        </div>
      </div>
    );
  }
}
state 渲染
import React, { Component } from "react";

export default class ListComp extends Component {
  constructor(props) {
    super(props);

    this.state = {
      lists: [
        {
          id: 1,
          shop_name: "汽车?"
        },
        {
          id: 2,
          shop_name: "衣服?"
        },
        {
          id: 3,
          shop_name: "零食?"
        }
      ]
    };
  }

  // renderItem = () => {
  //     const { lists } = this.state;
  //     return (
  //         lists.map((elm,i)=> {
  //             return <li key= { elm.id }>{elm.shop_name}</li>
  //         })
  //     )
  // }

  renderItem = () => {
    const { lists } = this.state;
    return lists.map(elm => <li key={elm.id}>{elm.shop_name}</li>);
  };
  render() {
    return (
      <div>
        <ul>{this.renderItem()}</ul>
      </div>
    );
  }
}
state 添加案例
import React, { Component } from "react";

export default class StateDemo extends Component {
  constructor(props) {
    super(props);

    this.state = {
      arr: [11, 22, 33, 44, 55]
    };
  }
  addItem = () => {
    this.setState(() => {
      this.state.arr.push(66);
      return {
        arr: this.state.arr
      };
    });
  };
  render() {
    const { arr } = this.state;
    return (
      <div>
        <h2>渲染数组</h2>
        <button onClick={this.addItem}></button>
        <ul>
          {arr.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      </div>
    );
  }
}


事件处理 Event

如何绑定事件
采用 on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick, React 里的事件是驼峰onClickReact 的事件并不是原生事件,而是合成事件

事件 handler 的写法 【 王牌 || 王者】

  1. 直接在 render 里写行内的箭头函数(不推荐)
import React, { Component } from "react";

export default class Event1 extends Component {
  render() {
    return (
      <div>
        <button
          onClick={() => {
            alert("张浩雨");
          }}
        >
          render中直接写箭头函数{" "}
        </button>
      </div>
    );
  }
}
  1. 在组件内使用箭头函数定义一个方法(推荐)
import React, { Component } from "react";

export default class Event2 extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 2000
    };
  }
  changeMoney = () => {
    this.setState({
      money: 9999
    });
  };
  render() {
    const { money } = this.state;
    return (
      <div>
        <button onClick={this.changeMoney}>
          在组件内使用箭头函数定义一个方法(推荐)
        </button>
        <p>{money}</p>
      </div>
    );
  }
}
  1. 直接在组件内定义一个非箭头函数的方法,然后在 render 里直接使用onClick={this.handleClick.bind(this)}(不推荐)
import React, { Component } from "react";

export default class Event3 extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 2000
    };
  }

  changeMoney() {
    this.setState({
      money: 8888
    });
  }
  render() {
    const { money } = this.state;
    return (
      <div>
        <button onClick={this.changeMoney.bind(this)}>
          直接在组件内定义一个非箭头函数的方法
        </button>
        <p>{money}</p>
      </div>
    );
  }
}
  1. 直接在组件内定义一个非箭头函数的方法,然后在 constructor 里 bind(this)(推荐)
import React, { Component } from "react";

export default class Event4 extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 2000
    };
    this.changeMoney = this.changeMoney.bind(this);
  }

  changeMoney() {
    this.setState({
      money: 7777
    });
  }
  render() {
    const { money } = this.state;
    return (
      <div>
        <button onClick={this.changeMoney}>
          直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)
        </button>
        <p>{money}</p>
      </div>
    );
  }
}

注意: 事件不能定义在函数式组件中

Event 对象

和普通浏览器一样,事件 handler 会被自动传入一个 event 对象,这个对象和普通的浏览器 event 对象所包含的方法和属性都基本一致。不同的是 React 中的 event 对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagationevent.preventDefault 这种常用的方法

import React, { Component } from "react";

export default class EventObj extends Component {
  enentHandle = ev => {
    console.log("张浩雨: EventObj -> ev", ev);
    console.log("张浩雨: EventObj -> ev.target", ev.target);
  };
  render() {
    return (
      <div>
        <button onClick={this.enentHandle}>Event 对象</button>
      </div>
    );
  }
}

事件对象中的值很多都是 null,但是可以正常使用

事件的参数传递

  1. render里调用方法的地方外面包一层箭头函数
import React, { Component } from "react";

export default class EventArgu1 extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 0
    };
  }
  changeMoney = val => {
    this.setState({
      money: val
    });
  };
  render() {
    const { money } = this.state;
    return (
      <div>
        <button
          onClick={() => {
            this.changeMoney(1000);
          }}
        >
          **`render`里调用方法的地方外面包一层箭头函数**{" "}
        </button>
        <p>money:{money}</p>
      </div>
    );
  }
}
  1. render里通过this.handleEvent.bind(this, 参数)这样的方式来传递
import React, { Component } from "react";

export default class Eventargu2 extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 0
    };
  }

  changeMoney = val => {
    this.setState({
      money: val
    });
  };

  render() {
    const { money } = this.state;
    return (
      <div>
        <button onClick={this.changeMoney.bind(this, 5555)}>
          {" "}`render`里通过`this.handleEvent.bind(this, 参数)`这样的方式来传递{" "}
        </button>
        <p> money有:{money} </p>
      </div>
    );
  }
}
    • 通过event传递

      处理用户输入

import React, { Component } from "react";

export default class UserInput extends Component {
  constructor(props) {
    super(props);

    this.state = {
      firstName: "",
      lastName: ""
    };
  }

  getVal = ev => {
    this.setState({
      [ev.target.name]: ev.target.value
    });
  };
  render() {
    const { firstName, lastName } = this.state;
    return (
      <div>
        姓: <input type="text" name="firstName" onInput={this.getVal} />
        <hr />
        名: <input type="text" name="lastName" onInput={this.getVal} />
        <p>
          欢迎:{firstName} {lastName}
        </p>
      </div>
    );
  }
}
  • 比较推荐的是做一个子组件, 在父组件中定义方法,通过props传递到子组件中,然后在子组件件通过this.props.method来调用

ref 绑定:

建议不要过量使用 ref , 会导致性能的浪费

  • 普通绑定
    
<input type="text" ref="user" />
  • 函数形式 - 推荐(减少一次系统查找)
    
<input type="text" ref={el => (this.user = el)} />

案例

import React, { Component } from "react";
import Hello from "./Hello";

export default class RefComp extends Component {
  getRef = () => {
    console.log("ref", this);
    this.refs.ele.style.background = "red";
    this.item.style.background = "yellow";
  };
  render() {
    return (
      <div>
        <button onClick={this.getRef}>get ref 链</button>

        <p ref="ele"> 今天周四了,一周要过去了 </p>
        <Hello ref="comp"></Hello>
        <p ref={el => (this.item = el)}>ref 函数形式</p>
      </div>
    );
  }
}

组件通信

1. 父子组件通信

无论父组件传递是 props 还是 state,子组件都是通过 props 接收

  1. 父组件在子组件标签上写属性传餐
// Father.jsx
import React, { Component } from "react";
import Son from "./Son";

export default class Father extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 2000
    };
  }

  render() {
    const { money } = this.state;
    return (
      <div>
        <Son money={money} />
      </div>
    );
  }
}
  1. 子组件用 this.props 接收参数
// Sonn.js
import React, { Component } from "react";

export default class Son extends Component {
  render() {
    const { money } = this.props;
    return (
      <div>
        <p> 老爸给了我 {money} 生活费 </p>
      </div>
    );
  }
}
  1. 子父组件通信

    父组件传递方法给子组件,子组件调用父组件传递过来的方法
    注意: 自己的状态自己更改

  2. 父组件把操作自身的属性的事件处理程序 以属性的形式发给子组件
//Father.jsx
import React, { Component } from "react";
import Son from "./Son";

export default class Father extends Component {
  constructor(props) {
    super(props);

    this.state = {
      money: 0
    };
  }
  handle = val => {
    // 定位
    this.setState({
      money: val
    });
  };
  render() {
    const { money } = this.state;
    return (
      <div>
        <Son handle={this.handle}></Son> //定位
        <p>儿子给我了:{money} 块钱</p>
      </div>
    );
  }
}
  1. 子组件以 this.props 接收参数父组件的, 在一定条件下 执行父组件的事件处理程序 并进行传参
//Son,jsx
import React, { Component } from "react";

export default class Son extends Component {
  constructor(props) {
    super(props);

    this.state = {
      letter: 88888
    };
  }

  render() {
    const { handle } = this.props;
    const { letter } = this.state;
    return (
      <div>
        <button
          onClick={() => {
            handle(letter);
          }}
        >
          发红包
        </button>
      </div>
    );
  }
}
非父子组件通信
  1. 父组件定义 事件处理函数,并将其发送给 出发地组件
// Father.jsx
import React, { Component } from "react";
import Son from "./Son";
import Girl from "./Girl";

export default class Father extends Component {
  kick = () => {
    //? 定义改变son组件的事件处理函数
    this.son.changeFlag(); //todo 改变son组件的事件处理函数
  };

  render() {
    return (
      <div>
        <h4>这里是Father</h4>
        {/* 将事件处理函数发给girl组件 */}
        <Girl kick={this.kick} />
        <Son ref={el => (this.son = el)} />
      </div>
    );
  }
}
  1. 出发地组件 通过 this.props 组件接收 父组件的事件处理函数,并在一定条件下操作,触发父组件的事件处理函数
//Girl.jsx
import React, { Component } from "react";

export default class Girl extends Component {
  render() {
    const { kick } = this.props; //? girl 组件 接收父组件发过来的 事件处理函数
    return (
      <div>
        <h4>这里是girl</h4>
        {/* 在一定条件下执行父组件的事件处理函数 */}
        <button onClick={kick}> 揍弟弟 </button>
      </div>
    );
  }
}
  1. 目的地组件 用 ref 的定义 ,父组件的事件处理函数运行 通过 this.refs 找到并操作目的地组件
//Son.jsx
import React, { Component } from "react";

export default class Son extends Component {
  constructor(props) {
    super(props);

    this.state = {
      flag: false
    };
  }

  changeFlag = () => {
    //todo son 组件自身的事件处理函数
    this.setState({
      flag: true
    });
  };

  render() {
    const { flag } = this.state;
    return (
      <div>
        <h4>这里是Son</h4>
        {(flag && <h3>哭哭哭哭哭</h3>) || <h3></h3>}
      </div>
    );
  }
}
跨组件通信

在 react 没有类似 vue 中的事件总线来解决这个问题,我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系。react 提供了context api 来实现跨组件通信, React 16.3 之后的contextapi 较之前的好用。

  • 1.首先创建上下文 const MoneyContext = createContext( 默认值 )
    ? 在目的地组件 static 中 以属性 context 接收 moneyContext 并通过 this.context 来使用
    ? 在出发地组件创建<moneyContext.provider > 并以 value 属性进行传参
import React, { Component, createContext } from "react";

//* 1.首先引入createContext方法 并创建上下文 const MoneyContext = createContext( 默认值 )
const moneyContext = createContext(0);

//? 在目的地组件static 中 以属性context 接收moneyContext 并通过this.context 来使用
class Son extends Component {
  //? 子组件
  static contextType = moneyContext;
  render() {
    return (
      <div>
        <h4>这里是子组件</h4>
        <p>爷爷给了我{this.context}</p>
      </div>
    );
  }
}

class Father extends Component {
  //? 父组件
  render() {
    return (
      <div>
        <h4>这里是父组件</h4>
        <Son></Son>
      </div>
    );
  }
}

//? 在出发地组件创建<moneyContext.provider > 并以value属性进行传参
export default class Stride extends Component {
  //? 祖父组件
  constructor(props) {
    super(props);

    this.state = {
      money: 8888
    };
  }

  render() {
    const { money } = this.state;
    return (
      <div>
        <h4>这里是Grandfather组件</h4>
        <moneyContext.Provider value={money}>
          <Father></Father>
        </moneyContext.Provider>
      </div>
    );
  }
}