React prop类型检查与Dom

简介: 随着应用规模的增长以及参与开发的人员越来越多,组件模块之间相互调用出现的BUG的情况会呈指数级别的增长,这时我们可以引入传递参数的检测与限定机制来减轻这个问题。React提供了Props参数检查的机制,通过这个机制我们可以限定使用者在使用组件时的传递参数。

使用PropTypes进行类型检查

当应用不断增长时,可以用过类型检查发现很多bug。对于某些应用,可以使用JavaScript扩展工具来完成,比如使用  Flow 或 TypeScript 来检查整个工程。除了引入外部工具之外,React也提供了参数类型检查的功能,只需要为每一个属性指定一个 propTypes 即可:

// 15.5之后,需要单独引入依赖才能使用类型检查
import PropTypes from 'prop-types';
//定义组件
class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

//指定类型检查
Greeting.propTypes = {
  name: React.PropTypes.string
};

PropTypes将会设定一系列验证器,这些验证器用于确保组件接受到的参数(props)是指定的类型。比如上面的例子,当一个错误的类型被组件接收到,会有一段警告内容使通过console输出。propsTypes仅仅在开发模式下使用

PropTypes

以下是各种验证器的示例:

MyComponent.propTypes = {
  // 指明每个传入参数的具体类型,传递的参数仅限于这些JavaScript的内置类型
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // number、string、element或者一个列表都是允许的
  optionalNode: PropTypes.node,

  // 接收一个React组件
  optionalElement: PropTypes.element,

  // 声明这个参数只接收某个对象(class)的实例,适用于传递一个对象作为配置参数的
  optionalMessage: PropTypes.instanceOf(Message),

  // 指定参数限定在多个对象之内
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 指定参数允许多个类型
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 指定类型的列表
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 指定传递某个类型,是一个对象不是数据本身
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 指定传递参数的结构,适用于传递一个对象时限定对象的结构
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // 表明这个参数是必须要传递的参数,在使用这个组件时,这个参数必须传入数据
  requiredFunc: PropTypes.func.isRequired,

  // 允许任何类型的数据。
  requiredAny: PropTypes.any.isRequired,

  // 指定一个自定义的检查器,当检查失败时需要返回一个Error对象来指明错误。
  // 错误只需要返回,切记不能使用throw或console.warn输出
  // 不适用于 oneOfType 类型。
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // 用于检测一个数组传递的自定义检查器,适用于arrayOf和objectOf类型。
  // 当出现检查错误时需要返回Error
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

限定至少接收一个子元素

可以使用 PropTypes.element 来指明组件必须接收一个子元素:

class MyComponent extends React.Component {
  render() {
    // This must be exactly one element or it will warn.
    const children = this.props.children;
    return (
      <div>
        {children}
      </div>
    );
  }
}

MyComponent.propTypes = {
  children: React.PropTypes.element.isRequired
};

设定props默认值

还可以使用 defaultProps来指定默认值:

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

// 指定props.name的默认值:
Greeting.defaultProps = {
  name: 'Stranger'
};

ReactDOM.render(
  <Greeting />,
  document.getElementById('example')
);

Refs和真实Dom

在典型的React数据流中,props参数传递的唯一接口。当需要修改参数时,必须修改props值并重新渲染(render)。然而,有很多场景需要在单向数据流之外修改子组件,React提供“Refs”特性来直接修改真实Dom元素。

什么时候需要使用Refs

当遇到以下情况时,建议使用Refs特性:

  • 需要管理聚(focus)、文档选择或媒体回放等真实Dom事件时。
  • 触发需要马上执行的动画。
  • 引入第三方库时。

避免将Refs用于任何声明性的工作,如使用一个props.isOpen参数来代替Dialog的open()和close()接口。

将Ref添加到Dom元素中

React支持在任何组件上使用ref。ref属性提供一个回调方法,当组件被渲染或被移除后,这个回调方法会被调用。

当ref属性用于一个HTML元素时,ref的回调方法会获取Dom的实例。例如,下面的例子获取到input标签的Dom实例并保存到this.textInput变量中,这个变量一直指向Dom节点。

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.focus = this.focus.bind(this);
  }

  // 定义一个focus方法
  focus() {
    this.textInput.focus();
  }

  render() {
    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focus}
        />
      </div>
    );
  }
}

当Dom元素被渲染后,React会回调ref指定的方法,并传递当前Dom的实例作为参数,当Dom被移除时,ref指向的方法也会被调用,传入的参数为null。

使用ref回调方法来设置class的属性是获取真实Dom对象的常用方法,上面的例子给出了一个编写方式,只要语法正确你可以用各种方式来编写,如更简短的: ref={input => this.textInput = input}

给class组件增加一个Ref属性

当ref用于一个由class关键字声明的自定义组件时,ref指向的回调方法会在组件完成渲染后被回调,传递的参数是组件的实例。例如下面的例子,在 CustomTextInput 组件完成渲染后立即模拟一次focus事件:

class AutoFocusTextInput extends React.Component {
  componentDidMount() {//完成渲染后被回调
    this.textInput.focus();//聚焦到当前组件
  }

  render() {
    // CustomTextInput 已经在上一段代码中声明
    return (
      <CustomTextInput
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

必须用class来定义 CustomTextInput 组件才会生效。

给Function声明的组件设定Refs

不能再function定义的组件直接使用ref,因为在声明时他并没有实例化:

function MyFunctionalComponent() {
  return <input />;
}

class Parent extends React.Component {
  render() {
    // 错误,这里的ref不会有任何效果。
    return (
      <MyFunctionalComponent
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

最合理的方式是将function定义的组件转换为class,这和我们需要使用state来控制状态是一个道理。不过在function组件中,如果内部引用的是另一个class组件也是可以使用Refs特性的:

function CustomTextInput(props) {
  // 在这里声明textInput,每次重新渲染时,都会新生成一个本地变量
  let textInput = null;

  // 每次重新渲染时,都会新生成一个回调方法
  function handleClick() {
    textInput.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

切勿过度使用Refs特性

可能在了解Refs的机制后,某些开发人员更倾向于在代码中使用Refs这种“操作即发生”特性来实现功能。但是在使用之前最好多花点时间来思考为什么状态需要由不同的组件层次来控制,通常情况下组件之间的状态最好由他们共同的祖先来控制: React 状态、事件与动态渲染

*使用警告

如果ref的回调方法被定义为一个内联方法,它在更新之前会发生2次调用,第一调用时会传递一个null值,第二次会赋予真正的Dom对象。这是因为在每次渲染时都会有一个新的方法实例被创建所以React必须清除已有的ref并创建一个的ref。可以通过将ref回调方法定义为类的绑定方法来避免这种情况,但请注意,在大多数情况下,这并不会导致什么问题。


相关文章
|
3月前
|
JavaScript 前端开发 算法
React技术栈-虚拟DOM和DOM diff算法
这篇文章介绍了React技术栈中的虚拟DOM和DOM diff算法,并通过一个实际案例展示了如何使用React组件和状态管理来实现动态更新UI。
46 2
|
4月前
|
JavaScript 前端开发 算法
react中虚拟dom和diff算法
在React中,虚拟DOM(Virtual DOM)和Diff算法是两个核心概念,它们共同工作以提高应用的性能和效率。
46 4
|
1月前
|
前端开发 安全
如何使用类型参数来创建 React 泛型组件?
通过以上步骤,就可以使用类型参数来创建灵活、可复用且类型安全的React泛型组件,以满足不同的数据类型和业务需求。
|
3月前
|
JavaScript 前端开发
react学习(3)创建虚拟dom的两种方式
react学习(3)创建虚拟dom的两种方式
172 67
|
2月前
|
JavaScript 前端开发 算法
React 虚拟 DOM 深度解析
【10月更文挑战第5天】本文深入解析了 React 虚拟 DOM 的工作原理,包括其基础概念、优点与缺点,以及 Diff 算法的关键点。同时,分享了常见问题及解决方法,并介绍了作者在代码/项目上的成就和经验,如大型电商平台的前端重构和开源贡献。
65 3
|
2月前
|
JavaScript 前端开发 安全
使用 TypeScript 加强 React 组件的类型安全
【10月更文挑战第1天】使用 TypeScript 加强 React 组件的类型安全
40 3
|
3月前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
34 3
|
3月前
|
JavaScript 前端开发
react字符串转为dom标签,类似于Vue中的v-html
本文介绍了在React中将字符串转换为DOM标签的方法,类似于Vue中的`v-html`指令,通过使用`dangerouslySetInnerHTML`属性实现。
108 0
react字符串转为dom标签,类似于Vue中的v-html
|
4月前
|
前端开发 JavaScript
React 中的不同类型组件
【8月更文挑战第31天】
32 0
|
4月前
|
前端开发 测试技术
React 中 Render Prop 的概念
【8月更文挑战第31天】
34 0