React学习5—— 高阶应用:prop类型检查与真实Dom操作

使用PropTypes进行类型检查

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

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

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

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

React.PropTypes

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

MyComponent.propTypes = {
  // 指明每个传入参数的具体类型,传递的参数仅限于这些JavaScript的内置类型
  optionalArray: React.PropTypes.array,optionalBool: React.PropTypes.bool,optionalFunc: React.PropTypes.func,optionalNumber: React.PropTypes.number,optionalObject: React.PropTypes.object,optionalString: React.PropTypes.string,optionalSymbol: React.PropTypes.symbol,// number、string、element或者一个列表都是允许的
  optionalNode: React.PropTypes.node,// 接收一个React组件
  optionalElement: React.PropTypes.element,// 声明这个参数只接收某个对象(class)的实例,适用于传递一个对象作为配置参数的
  optionalMessage: React.PropTypes.instanceOf(Message),// 指定参数限定在多个对象之内
  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),// 指定传递参数的结构,适用于传递一个对象时限定对象的结构
  optionalObjectWithShape: React.PropTypes.shape({
    color: React.PropTypes.string,fontSize: React.PropTypes.number
  }),// 表明这个参数是必须要传递的参数,在使用这个组件时,这个参数必须传入数据
  requiredFunc: React.PropTypes.func.isRequired,// 允许任何类型的数据。
  requiredAny: React.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: React.PropTypes.arrayOf(function(propValue,key,componentName,location,propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

限定至少接收一个子元素

可以使用React.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这种“操作即发生”特性来实现功能。但是在使用之前最好多花点时间来思考为什么状态需要由不同的组件层次来控制,通常情况下组件之间的状态最好由他们共同的祖先来控制,建议详细阅读“状态共享”相关的说明。

*使用警告

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom