React 之 state 与 表单受控

作者 糖一瓶 日期 2018-03-27
React 之 state 与 表单受控

一、组件内部的state

1.什么是state?

state叫做状态,是每一个类组件都有的属性。函数式组件,没有state。
在任何类组件的构造函数里面,可以用this.state = {}来给类的实例添加state属性。
在render里面,可以用{this.state.a}这样的插值来显示出每一个属性的值。

import React from "react";
import ReactDOM from "react-dom";

export default class App extends React.Component {
    constructor() {
        super();

        this.state = {
            a : 100
        }
    }

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

2.setState()

组件的this.setState()函数可以用来更改state的值,当state变化的时候,组件视图会自动更新。

    import React from "react";
    import ReactDOM from "react-dom";

    export default class App extends React.Component {
        constructor() {
            super();

            this.state = {
                a : 100
            }
        }

        render() {
            return <div>
                <button onClick={()=>{
                    this.setState({
                        a : this.state.a + 1
                    })
                       }}>按我</button>
            <h1>{this.state.a}</h1>
        </div>
    }
}
  • 我们react中的事件监听,要写在标签上(虽然之前说,前端3层要求分离,要求html、js、css尽量分离,但是react又将事件监听写在了标签上)。
  • 事件名onClick而不是onclick,注意大写字母C。因为react将每一个事件名都进行了拓展,所以onClick是react自己的方法。同理,所有事件名的on后面的首字母都是大写:onMouseEnter、onDoubleClick、onKeyDown。
  • onClick=后面紧跟{},表示插值。大括号里面,是一个箭头函数。注意这个函数必须是箭头函数,否则this错误。
    <button onClick={()=>{}}></button>
    
  • setState是定义在React.Component类中的方法,所以我们的任何一个组件都能调用this.setState()。表示“设置state”。语法是
    this.setState({
        要设置的k : 新的v
    });
    
  • setState不仅仅能够改变实例的state的属性值,而且能够产生视图刷新。而如果不用setState(),只是让state的属性值进行改变,此时视图是不刷新的。

3.提炼出事件处理函数

我们之前的onClick后面直接跟上了{()=>{}}。实际上可以提炼出来。

写法1:

import React from "react";
import ReactDOM from "react-dom";

export default class App extends React.Component {
    constructor() {
        super();

        this.state = {
            a : 100
        }
    }

    //单击事件的处理函数
    clickHandler(){
        this.setState({
            a : this.state.a + 1
        });
    }

    render() {
        return <div>
            <button onClick={this.clickHandler.bind(this)}>按我</button>

            <h1>{this.state.a}</h1>
        </div>
    }
}
  • 提炼成为组件的方法(实际上写在了构造器的prototype上,实例的原型上),在onClick调用的时候,必须写bind(this),将调用的这个函数的上下文绑定为组件的实例。可以当做是一个固定的语法!

写法2(若想要传参)

如何传参数?

import React from "react";
import ReactDOM from "react-dom";

export default class App extends React.Component {
    constructor() {
        super();

        this.state = {
            a : 100
        }
    }

    //单击事件的处理函数
    clickHandler(n){
        this.setState({
            a : this.state.a + n
        });
    }

    render() {
        return <div>
            <button onClick={()=>{this.clickHandler(1)}}>按我</button>
            <button onClick={()=>{this.clickHandler(2)}}>按我</button>
            <button onClick={()=>{this.clickHandler(3)}}>按我</button>
            <button onClick={()=>{this.clickHandler(4)}}>按我</button>

            <h1>{this.state.a}</h1>

            <div style=<!--0-->></div>
        </div>
    }
}

三、表单受控

  • 我们现在给组件设置一个state,希望文本框和这个state实时相等。
  • 我们说,这个文本框和state进行了双向数据绑定,react中称为这个文本框“受控”了。
  • 就是说当state改变的时候文本框的内容改变,而表单内容改变数据也会改变,实现了双向绑定。

value onChange形式的表单受控

此时就要记住 value onChange (一说到受控就该想到)

import React from "react";
import ReactDOM from "react-dom";

export default class App extends React.Component {
    constructor() {
        super();

        this.state = {
            a : "我爱你"
        }
    }

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

            <input type="text" value={this.state.a} onChange={(e)=>{
                this.setState({
                    a : e.target.value
                })
            }}/>
        </div>
    }
}
  • 单行文本、多行文本、range条、select下拉菜单的受控是value onChange

其他表单受控

1.单选按钮radio

<p>
    <label><input type="radio" name="sex" checked={this.state.sex == "男"} onChange={(e)=>{
        this.setState({sex : "男"})
    }}/>男</label>
    <label><input type="radio" name="sex" checked={this.state.sex == "女"} onChange={(e) => {
        this.setState({ sex: "女" })
    }}/>女</label>
    <label><input type="radio" name="sex" checked={this.state.sex == "保密"} onChange={(e) => {
        this.setState({ sex: "保密" })
    }}/>保密</label>
</p>

2.复选框checkbox

<p>
    <label>
        <input type="checkbox" checked={this.state.tongyixieyi} onChange={(e)=>{
            this.setState({tongyixieyi : e.target.checked})
        }}/> 我已阅读上面的协议
    </label>
</p>

如果复选框是一组,比如让你选择自己的爱好,此时就要和一个数组值进行受控双向绑定:
套路就是checked里面用includes判断是否在数组中,onChange来判断区别是加项还是减项。

//增加爱好
addHobby(item){
    this.setState({
        hobby : [...this.state.hobby , item]
    });
}

//删除某一个爱好
delHobby(item) {
    this.setState({
        hobby: this.state.hobby.filter(_item => _item != item)
    });
}
<p>
    <label>
        <input type="checkbox" checked={this.state.hobby.includes("篮球")} onChange={(e)=>{
            if(e.target.checked){
                this.addHobby("篮球");
            }else{
                this.delHobby("篮球");
            }
        }}/>篮球
    </label>
    <label>
        <input type="checkbox" checked={this.state.hobby.includes("足球")} onChange={(e) => {
            if (e.target.checked) {
                this.addHobby("足球");
            } else {
                this.delHobby("足球");
            }
        }}/>足球
    </label>
    <label>
        <input type="checkbox" checked={this.state.hobby.includes("羽毛球")} onChange={(e) => {
            if (e.target.checked) {
                this.addHobby("羽毛球");
            } else {
                this.delHobby("羽毛球");
            }
        }}/>羽毛球
    </label>
</p>

四、非受控组件ref标记

我们现在遇见一个input框,就喜欢和state进行双向数据绑定,简称受控。实际上,也可以不受控。
不受控的表单元素,往往都要加上ref标记,然后用this.refs.***来得到它。

import React from "react";
import ReactDOM from "react-dom";
import classnames from "classnames";

export default class App extends React.Component {
    constructor() {
        super();

    }

    render() {

        return <div> 
            <p>
                <input type="text" ref="kuang"/>
            </p>
            <p>
                <button onClick={()=>{
                    alert(this.refs.kuang.value)
                }}>按我</button>
            </p>
        </div>
    }
}
  • ref就是id,一个组件中,不能有相同名字的ref。但是this.refs.**一定选择的是本组件的东西,不会选择其他组件的东西。

五、组件之间的数据传递

目录结构如下:


目录结构如下:
test
|-node_modules
|-www
|-app
|-main.js
|-App.js
|-Child.js
|-index.html
|-webpack.config.js
|-package.json

父子关系:
main.js(父)中引入App.js(子); App.js(父)中引入Child.js(子)

认识props

  • 在父组件中,通过在子组件的标签上罗列k=v形式的参数,以达到给子组件传值的目的。
  • 在子组件中用{this.props.**}的形式,接收父亲传入的参数。
  • props是properties属性的简写。
    5
    3

props是只读的,子组件不能修改props

下面的代码是错误的:

return <div> 
    <h1>我是子组件,我收到了父亲的参数:{this.props.a}</h1>
    <button onClick={()=>{
        this.props.a ++;
    }}>按我</button>
</div>

4

子组件若想修改父亲的值怎么办

方法:父亲一并传入函数,儿子调用父亲的函数,改变父亲的值
父组件App.js:
除了传给儿子a值以外,还传了设置a值的函数给儿子。

import React from "react";
import ReactDOM from "react-dom";
import classnames from "classnames";

import Child from "./Child.js";

export default class App extends React.Component {
    constructor() {
        super();

        this.state = {
            a : 10
        }
    }

    setA(a){
        this.setState({a});
    }

    render() {

        return <div> 
            <h1>我是父组件{this.state.a}</h1>
            <Child a={this.state.a} setA={this.setA.bind(this)}></Child>
        </div>
    }
}

子组件Child.js
如果要改变父亲的值,就要调用父亲传给自己的函数。

import React from "react";
import ReactDOM from "react-dom";
import classnames from "classnames";

export default class Child extends React.Component {
    constructor() {
        super();

    }

    render() {

        return <div> 
            <h1>我是子组件,我收到了父亲的参数:{this.props.a}</h1>
            <button onClick={()=>{
                this.props.setA(this.props.a + 1);
            }}>按我</button>
        </div>
    }
}

所以本质上,改变父亲的值,依然是父亲自己的函数在实现。

只对父亲负责!不对兄弟负责!

react编程必须养成的思维:组件只有父子之间有关系,兄弟和兄弟之间没有任何关系!兄弟和兄弟之间不需要考虑彼此对彼此造成的任何的影响,只需要考虑对父亲的影响即可。