② React 面向组件编程(state、props、refs)、事件处理

查看专栏其它文章:

① React 介绍及JSX简单使用



本人是个新手,写下博客用于自我复习、自我总结。
如有错误之处,请各位大佬指出。
学习资料来源于:尚硅谷


组件的理解

在具体说到后续内容前,需要理解何为 “组件”

  1. 组件是用来实现特定( 局部 )功能效果的代码集合( html/css/js )

  2. 为什么需要组件:因为一个界面需要实现的功能可能会比较多,都放在一起去管理可能会比较复杂,将它们按功能 / 按区域 分成多个组件会清晰很多。不仅如此,将其划分成多个组件还有助于复用代码提高运行效率

  3. 当应用是以多组件的方式实现, 这个应用就是一个组件化的应用

在平常的学习中,组件化可能不太受重视,但在工作中,组件化将是重要的。为了更好的解释,先简单举个例子来分析一下:
在这里插入图片描述
(文字较多,不感兴趣可快速略过)

假如我们想建一个上图中的页面,如果不考虑逻辑,相信大家有能力模仿出来。初学者可能会这样,比如:用户名那个红框中的内容,初学者可能会一行一行的模仿,先定位用户名,然后设置样式,然后定位输入框,然后设置样式…从上向下。但问题也就在这里发生:

(1)如果页面的内容有一部分需要位置上的变动,那可想而知每一行的定位都失效了。解决方法大家都能想到,就是将它们再用一个标签包裹,这就靠近了组件化的概念。我们继续考虑其它情况:

(2)如果一个页面包含的内容很多且复杂,那么页面的代码量就有了一定的数量。即使大部分开发工具都有找代码功能,但仍需多次比对。想要修改相关功能,也需要去htmlstylescript三部分中耐心寻找。如果能把这三部分中,用来构建一个功能的代码提取出来,显而易见,我们能快速锁定相关功能去进行修改,这将大幅度提高代码效率。所以这提取出来的部分,就是所谓组件。这也是为什么Vue3会出现Composition API(setup函数)这种集中管理的方式。那JS提取出来我们只要导入JS文件即可,那组件该如何使用将会在下文中演示。这么做之后还有其它好处:

(3)在工作小组中,通常是用Git (或其它方式)来更新代码。如果真的要让多位成员,完善一个页面中的多个功能(通常是一人管理多个相似页面),而这些功能都在同一段代码里,极有可能发生代码覆盖问题。除此以外,一旦代码管理人员交替,如果不把重要功能抽成组件,那么想找到某功能犹如大海捞针。除了代码更清晰以外,组件化也有助于复用代码,我们考虑这个情况:

(4)如果相关功能在其他页面里也反复用到,举个最简单的例子,比如一个设计好样式的按钮,我想让该项目中的所有按钮都使用这个按钮,那与其在每个页面中都去反复写,不如抽取出来变成公共组件,之后只需要去调用一下即可。

更具体一些的就可以参考,比如:Element-UI、v-chart、VUX 等一系列有助于我们开发的 “工具”,在它们的官网中介绍自己就是:“组件库” 或 “插件”,那里面的每个小功能(比如:按钮、搜索框、图标 等等)就是 “组件”。在这些组件里就帮我们封装好了相关代码,我们直接去按方式调用就可以了。

说到这里相信大家已经对组件有所了解。至于更多使用方面的内容,将在下文介绍。


再说个题外话,还有一个概念和组件很像,那就是模块

模块

  1. 理解:向外提供特定功能的js程序,一般就是一个js文件

  2. 为什么需要模块: 因为在项目中JS代码可能会有很多,都放在一起去管理可能会比较复杂。也就是说,它和组件其实大同小异,作用都是复用代码,但模块是简化JS,提高JS的运行效率。而组件复用的代码包括 HTML、CSS 和 JS

  3. 当应用的JS都以模块来编写, 这个应用就是一个模块化的应用

也就是说,模块和组件都能方便我们在开发过程中对代码的管理和调整。


React面向组件编程


自定义组件的使用

定义组件共有两种方法。


方式1: 工厂函数组件(简单组件)

从最简单的角度出发,我们可以利用函数function,将某功能的代码放入其中,这样就能做到简单区分各部分代码的功能:

function MyComponent () {
   return <h2>工厂函数组件(简单组件)</h2>
}

现在这个函数MyComponent就是一种简单的组件,它里面存放了相关功能,并用return把内容返回出来。我们想使用这个组件,依然使用ReactDOM.render,但是这个组件我们要以标签的形式去渲染:

ReactDOM.render(<MyComponent/>, document.getElementById('example1'))

方式2: ES6类组件(复杂组件)

学过JS的肯定不会对上面function感到陌生,但到了ES6中,ES6提供了更接近传统语言(比如C++和Java)的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

如果从未接触过Class,并对往后的用法感到困惑,可以参考文章:ES6 class

那现在我们就使用class来替代function。为了更方便使用,React 提供了这种 定义组件的方式:

class MyComponent2 extends React.Component {
  render () {
    return <h2>ES6类组件(复杂组件)</h2>
  }
}

同样地,我们需要渲染组件标签:

ReactDOM.render(<MyComponent2/>, document.getElementById('example2'))

需要额外说明的是:ES6的类,完全可以看作构造函数的另一种写法,那使用的时候,也是直接对类使用new命令(创建实例对象),跟构造函数的用法完全一致:

class Bar {
  doStuff() {
    console.log('stuff');
  }
}

var b = new Bar();
b.doStuff() // "stuff"

也就是说,我们想使用类内部定义的方法需要创建实例对象。我们做以下操作:

class MyComponent2 extends React.Component {
  render () {
    console.log(this) // MyComponent2的实例对象
    return <h2>ES6类组件(复杂组件)</h2>
  }
}

console.log的结果就是类的实例对象,也就是this现在指向MyComponent2的实例对象。
在这里插入图片描述
因此,我们就可以在render()里使用类中其它方法。具体有什么用,在后续就可以看到。


完整代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>component_basic</title>
</head>
<body>
  <div id="example1"></div>
  <div id="example2"></div>

  <script type="text/javascript" src="../js/react.development.js"></script>
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <script type="text/javascript" src="../js/babel.min.js"></script>

  <script type="text/babel">
    // 1. 定义组件
    /*方式1: 工厂函数组件(简单组件)*/
    function MyComponent () {
      return <h2>工厂函数组件(简单组件)</h2>
    }
    /*方式2: ES6类组件(复杂组件)*/
    class MyComponent2 extends React.Component {
      render () {
        return <h2>ES6类组件(复杂组件)</h2>
      }
    }
    // 2. 渲染组件标签
    ReactDOM.render(<MyComponent/>, document.getElementById('example1'))
    ReactDOM.render(<MyComponent2/>, document.getElementById('example2'))
  </script>
</body>
</html>

从结果来看,这些内容确实被渲染出来了,并且是自定义的组件标签:
在这里插入图片描述

注意

  1. 组件名必须首字母大写

  2. 虚拟DOM元素只能有一个根元素

  3. 虚拟DOM元素必须有结束标签


组件属性①: state 及 事件处理

在说到后续内容前,需要先说一下 React 的事件处理。推荐文章:React 事件处理。该文章写的很清晰,细节内容就不在这里赘述了。主要内容如下:

React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:

  1. React 事件绑定属性的命名采用驼峰式写法,而不是小写。

  2. 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)。

比如:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();  // 如果想阻止默认行为,必须明确使用preventDefault
    console.log('链接被点击');
  }
 
  return (
    // 需要传入一个函数作为事件处理函数
    <a href="#" onClick={handleClick}>
      点我
    </a>
  );
}

然后将事件转到ES6 class语法前,需要先了解一下ES6类组件更多的用法。


React 把组件看成是一个状态机(State Machines)。通过与用户的交互,来实现不同状态,然后渲染 UI,让用户界面和数据保持一致。而这个状态 state 会在类的构造函数 constructor 中来初始化。

例:

class Like extends React.Component {
  constructor () {
    super()
    // state是组件对象最重要的属性, 值是对象(可以包含多个数据)
    // 组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
    this.state = {  // 初始化状态
      isLikeMe: true
    }
  }
  render () {
    // 之前提到的:this指向类的实例对象。因此可以直接使用构造函数中的state
    const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
    return <h2>{text}</h2>
  }
}

那在此基础上,我们将事件加进来。这个事件是点击 text 文字就能修改 stateisLikeMe 的值。修改之后,因为在 React 里,更新组件的 state,用户界面就会被重新渲染(不要操作 DOM),因此页面文字内容会因点击而变化。但是要想修改 state 中的值,需要使用 setState,不能直接:this.state.isLikeMe = !this.state.isLikeMe。因为如果直接修改 ,React 并不会重新渲染(render)页面。( 这是个很难解释且棘手的问题 。除此以外setState也可能会产生一些微妙的bug,感兴趣的可查阅文章:我不再使用React.setState的3个原因

例:

class Like extends React.Component {
  constructor () {
    super()
    this.state = {  // 初始化状态
      isLikeMe: true
    }
    // 绑定this为组件对象
    this.change = this.change.bind(this)
  }
  change () {
    // this.state.isLikeMe = !this.state.isLikeMe // 不能更新更新某个状态
    this.setState({
      isLikeMe: !this.state.isLikeMe
    })
  }
  render () {
    // this指向类的实例对象
    const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
    return <h2 onClick={this.change}>{text}</h2>
  }
}

在上面这段代码中,我们知道事件处理时,需要传入一个函数作为事件处理函数,因此 return 里的 change 必然是 类内部的方法,需要使用 this。但这里就会出现一个问题:类的方法默认不会绑定 this 。因此需要在构造函数 constructor 中绑定 this 。如果忘记绑定,当你调用这个函数的时候 返回的值会是 undefined。这并不是 React 的特殊行为。它是函数如何在 JavaScript 中运行的一部分。通常情况下,如果你没有在方法后面添加 () ,例如 onClick={this.handleClick},就应该为这个方法绑定 this。

如果使用 bind 觉得麻烦,这里有两种方式可以解决

(1)如果你正在使用实验性的属性初始化器语法,你可以使用属性初始化器来正确的绑定回调函数:

class Like extends React.Component {
  constructor () {
    super()
    this.state = {
      isLikeMe: true
    }
  }
  // 注意这里:
  change = () => {
    this.setState({
      isLikeMe: !this.state.isLikeMe
    })
  }
  render () {
    const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
    return <h2 onClick={this.change}>{text}</h2>
  }
}

(2)如果你没有使用属性初始化器语法,你可以在回调函数中使用 箭头函数:

class Like extends React.Component {
  constructor () {
    super()
    this.state = {
      isLikeMe: true
    }
  }
  change() {
    this.setState({
      isLikeMe: !this.state.isLikeMe
    })
  }
  render () {
    const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
    // 注意这里:
    return <h2 onClick={(e) => this.change(e)}>{text}</h2>
  }
}

使用(2)这个语法有个问题就是每次 Like 渲染的时候都会创建一个不同的回调函数。在大多数情况下,这没有问题。然而如果这个回调函数作为一个属性值传入低阶组件,这些组件可能会进行额外的重新渲染。通常建议 在构造函数中绑定 或 使用(1)语法 来避免这类性能问题


完整代码:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>component_state</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>

<script type="text/babel">
  class Like extends React.Component {
    constructor () {
      super()
      this.state = {  // 初始化状态
        isLikeMe: true
      }
      // 绑定this为组件对象
      this.change = this.change.bind(this)
    }
    change () {
      // this.state.isLikeMe = !this.state.isLikeMe // 不能更新更新某个状态
      this.setState({
        isLikeMe: !this.state.isLikeMe
      })
    }
    render () {
      const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
      // this指向类的实例对象
      return <h2 onClick={this.change}>{text}</h2>
    }
  }
  ReactDOM.render(<Like/>, document.getElementById('example'))
</script>
</body>
</html>

组件属性②: props

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。(组件内部不要修改props数据!)

每个组件对象都会有 props ( properties 的简写)属性,组件标签的所有属性都保存在props中

使用演示:

function HelloMessage(props) {
    return <h1>Hello {props.name}!</h1>;
}
 
const element = <HelloMessage name="CSDN"/>;
 
ReactDOM.render(element, document.getElementById('example'));

实例中 name 属性通过 props.name 来获取。

再转到ES6类组件:

class HelloMessage extends React.Component {
    render() {
        return (
            <h1>Hello, {this.props.name}</h1>
        );
    }
}

const element = <HelloMessage name="CSDN"/>;

ReactDOM.render(element, document.getElementById('example'));

需要说明的是,在这里能直接使用 this.props.name 并不是 React 希望我们这样做。在这里能使用,是因为 React 在组件实例化的时候,马上又给实例设置了一遍 props。完整写法应该是这样的:

class HelloMessage extends React.Component {
    constructor(props) {
        super(props)
        console.log(this.props) // 未传递props,输出 undefined ;传递props,输出 Object {(组件标签属性)}
    }
    render() {
        return (
            <h1>Hello, {this.props.name}</h1>
        );
    }
}

那是否意味着我们可以只写 super() 而不用 super(props) 呢?

不是的。虽然 React 会在组件实例化的时候设置一遍 props,但在 super 调用一直到构造函数结束之前,this.props 依然是未定义的。如果这时在构造函数中调用了函数,函数中有 this.props.xxx 这种写法,直接就报错了。所以推荐使用 构造函数就设置 props


默认props

我们可以通过组件类的 defaultProps 属性为 props 设置默认值,演示代码如下:

class HelloMessage extends React.Component {
    render() {
        return (
            <h1>Hello, {this.props.name}</h1>
        );
    }
}

HelloMessage.defaultProps = {
    name: 'CSDN'
};

const element = <HelloMessage/>;

ReactDOM.render(element, document.getElementById('example'));

当然,你也可以在设置了默认值的基础上继续传参,如果未传则显然默认值,否则显示传入的参数。

假如现在设置了多个默认值:

class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名: {this.props.name}</li>
                <li>性别: {this.props.sex}</li>
                <li>年龄: {this.props.age}</li>
            </ul>
        )
    }
}
// 指定属性的默认值
Person.defaultProps = {
    sex: '男',
    age: 18
}

现在的情况是:必须传入name,sex和age不传则显示默认值。

在组件标签中如果一个一个传,数据比较多的情况下比较难看,我们可以这么做:

(如果不了解扩展运算符可参考文章:es6 javascript对象的扩展运算符)

const person = {
    name: 'Tom',
    sex: '女',
    age: 18
}
//扩展属性: 将对象的所有属性通过props传递
ReactDOM.render(<Person {...person}/>, document.getElementById('example1'))

如果传递参数名称和默认值名称不同,那显然就只能一个一个传了。

const person2 = {
    myName: 'JACK',
    age: 17
}
ReactDOM.render(<Person name={person2.myName} age={person2.age}/>,
    document.getElementById('example2'))

prop-types

现在就可以用到之前提到却没用到的prop-types。
在这里插入图片描述
它可以帮我们在运行时检查React props里的属性类型。

<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>

Props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。

演示代码:

class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名: {this.props.name}</li>
                <li>性别: {this.props.sex}</li>
                <li>年龄: {this.props.age}</li>
            </ul>
        )
    }
}
// 对标签属性进行限制
Person.propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number
}
const person = {
    name: 'Tom',
    sex: '女',
    age: 18
}
ReactDOM.render(<Person {...person}/>, document.getElementById('example1'))

更多验证器说明如下:

MyComponent.propTypes = {
    // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
 
    // 可以被渲染的对象 numbers, strings, elements 或 array
    optionalNode: React.PropTypes.node,
 
    //  React 元素
    optionalElement: React.PropTypes.element,
 
    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: React.PropTypes.instanceOf(Message),
 
    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
 
    // 可以是多个对象类型中的一个
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),
 
    // 指定类型组成的数组
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
 
    // 指定类型的属性构成的对象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
 
    // 特定 shape 参数的对象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),
 
    // 任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,
 
    // 不可空的任意类型
    requiredAny: React.PropTypes.any.isRequired,
 
    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  }
}

组合使用 props + state (※)

以下实例演示了如何在应用中组合使用 state 和 props 。我们可以在父组件中设置 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据。

class WebSite extends React.Component {
  constructor() {
      super(); 
      this.state = {
        name: "baidu",
        site: "https://www.baidu.com"
      }
    }
  render() {
    return (
      <div>
        <Name name={this.state.name} />
        <Link site={this.state.site} />
      </div>
    );
  }
}

class Name extends React.Component {
  render() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
}
 
class Link extends React.Component {
  render() {
    return (
      <a href={this.props.site}>
        {this.props.site}
      </a>
    );
  }
}
 
ReactDOM.render(
  <WebSite/>,
  document.getElementById('example')
);

如果乍一看觉得无法理解,你可以这样看:父组件 WebSite 其实就是这样的:

class WebSite extends React.Component {
    constructor() {
        super();
        this.state = {
            name: "baidu",
            site: "https://www.baidu.com"
        }
    }
    render() {
        return (
            <div>
                <h1>{this.state.name}</h1>
                <a href={this.state.site}>{this.state.site}</a>
            </div>
        );
    }
}

我们把 h1 标签和 a 标签中的内容提取出来,放到了子组件中。但是在子组件当中显然没有 state,所以子组件中的 name / site 需要靠父组件的传递。在之前的内容里已经介绍了 props 的用法,我们只要将其改为组件标签,然后根据 命名 传递参数过去即可。

因为这段代码量比较小,你可能会觉得这么做很多余。但如果代码量比较大,里面有很多功能,我们这时根据功能把代码拆分成多个组件,就会清晰的多。


再看一个情景:假如子组件需要父组件所有的 props 参数,这个时候一个一个参数的写会很麻烦,比如:<Name name={this.props.name} url={this.props.url} .../>

那么我们该如何把父属性直接赋值给子组件的 props 参数呢?如下写法即可:<Name props={this.props}/>

这样写就非常简洁了,也就子组件和父组件都有了同样数据结构的 props 参数。

在这里需要补充说明的是:父组件和子组件的参数名推荐是相同的。因为如果它们的参数名是不同的,但是表示的值是同一个,那么当调试页面时,写这段代码的人可能还记得这个问题,但是其他人可能就会遇到问题,导致效率低下,不便维护。


面试题:请区别一下组件的props和state属性

  1. state: 组件自身内部可变化的数据

  2. props: 从组件外部向组件内部传递数据,组件内部只读不修改


组件属性③: refs

React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。

使用方法:

(1)绑定一个 ref 属性到 render 的返回值上:

<input ref="myInput"/>

(2)在其它代码中,通过 this.refs 获取支撑实例:

var input = this.refs.myInput;
var inputValue = input.value;

应用到类组件上:

class MyComponent extends React.Component {
  handleClick() {
    // 使用原生的 DOM API 获取焦点
    this.refs.myInput.focus();
  }
  render() {
    // 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
    return (
      <div>
        <input type="text" ref="myInput"/>
        <input
          type="button"
          value="点我输入框获取焦点"
          onClick={this.handleClick.bind(this)}
        />
      </div>
    );
  }
}
 
ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

但上面这个 api 已经过时了,下面的是最新的用法:(请保证 React 版本为较新版本)

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();  
  }
  focus = () => {
	this.myRef.current.focus();
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.myRef}/>
        <button onClick={this.focus}>focus</button>
      </div>
    );
  }
}

通过在class中使用React.createRef()方法创建一些变量,可以将这些变量绑定到标签的ref中。那么该变量的current则指向绑定的标签dom。

需要注意:React 并不推荐过度使用 ref如果能通过state做到的事情,就不应该使用 refs


综合例:

需求: 自定义组件, 功能说明如下:

  1. 点击按钮, 提示第一个输入框中的值

  2. 当第2个输入框失去焦点时, 提示这个输入框中的值

//定义组件
class MyComponent extends React.Component {
  constructor(props) {
    super(props) // 调用父类(Component)的构造函数
    // 将自定义的函数强制绑定为组件对象
    this.handleClick = this.handleClick.bind(this) // 将返回函数中的this强制绑定为指定的对象, 并没有改变原来的函数中的this
  }
  // 自定义的方法中的this默认为null
  handleClick () {
    // 得到绑定在当前组件对象上的input的值
    alert(this.msgInput.value)
  }
  handleBlur (event) {
    alert(event.target.value)
  }
  render () {
    //组件内的标签都可以定义ref属性来标识自己
    //在组件中可以通过this.msgInput来得到对应的真实DOM元素
    //作用: 通过ref获取组件内容特定标签对象, 进行读取其相关数据
    return (
      <div>
        <input type="text" ref={input => this.msgInput = input}/>{' '}
        <button onClick={this.handleClick}>提示输入数据</button>{' '}
        <input type="text" placeholder="失去焦点提示数据" onBlur={this.handleBlur}/>
      </div>
    )
  }
}
// 渲染组件标签
ReactDOM.render(<MyComponent />, document.getElementById('example'))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只爭朝夕不負韶華

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值