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中,组件自己的属性的变化不会引发视图的变化。
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;
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state
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;
-
定义state: 在构造函数中使用this.state属性即可
-
使用state :在JSX中{this.state.a}
-
改变state: this.setState({a : this.state.a + 1}); 不能写++,因为state属性值只读。
后面的Redux架构,所有组件自己的state越来越少用了,而是变为了Redux中的state,不要混淆。
定义在自定义组件标签上面的值,就是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;
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的值。如果要修改,用state来接收。
props属性可以被验证有效性:
npm install --save-dev prop-types
./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的精髓。
上下文的精髓是可以跨级传递数据的,爷爷组件可以直接传递数据到孙子组件:
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;
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;