微信号:frontshow

介绍:InfoQ大前端技术社群:囊括前端、移动、Node全栈一线技术,紧跟业界发展步伐。

为什么说React 16是开发者的福音?

2018-09-21 17:57 前端之巅
作者|Harsh makadia
译者|无明
就像人们对升级移动应用程序和操作系统会感到兴奋一样,开发人员对开发框架的升级也会感到兴奋。不同框架的新版本总会带来开箱即用的新功能。以下是从 React 15 升级到 React 16 时需要知道的一些很棒的功能。是时候跟 React15 说再见了!
错误处理

React 16 引入了错误边界的概念。

错误边界是一种 React 组件,可以捕获子组件树中的 JavaScript 错误。它们会记录这些错误,并在错误 UI 上显示出来。错误边界会在渲染期间、生命周期方法中以及整个树的构造函数中捕获错误。

如果一个类组件定义了一个名为 componentDidCatch(error,info) 的生命周期方法,那么它就会成为错误边界:


  render() {
    if (this.state.hasError) {
      // 你可以渲染定制错误 UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

然后,你可以将其作为常规组件来使用。

<ErrorBoundary>  
   <MyWidget />
</ErrorBoundary>

componentDidCatch() 方法就像 JavaScript 的 catch{}块,只是用在了组件上。只有类组件可以成为错误边界。实际上,大多数情况下,你每次只需要声明一个错误边界组件,然后就可以在整个应用程序中使用它。

请注意,错误边界只会捕获位于它们之下的组件中的错误。错误边界无法捕获到自身的错误。如果错误边界渲染错误消息失败,错误将被传播到上方最接近的错误边界。这也类似于 JavaScript 中的 catch{}块。

打开这个链接查看演示:

https://codepen.io/gaearon/pen/wqvxGa

更多有关错误处理的信息,可以访问:

 https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html

新的 render 返回类型:片段和字符串

在渲染时可以摆脱将组件包装在 div 中。

你现在可以从组件的 render 方法返回元素数组。与其他数组一样,你需要为每个元素添加一个键以避免发出键警告:

render() {
  // 不需要将清单项包装在额外的元素中!
  return [
    // 不要忘了分配不同的键 :)
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

从 16.2.0 版本开始,React 支持片段语法,不再需要键。

支持返回字符串:

render() {
  return 'Look ma, no spans!';
}
Portal

Portal 提供了一种将子组件渲染为存在于父组件的 DOM 层次结构之外的 DOM 节点的方法。

ReactDOM.createPortal(child, container)

第一个参数(child)是可渲染的 React 子节点,如元素、字符串或片段。第二个参数(container)是一个 DOM 元素。

在从组件的 render 方法返回一个元素时,它将作为最近父节点的子节点挂载到 DOM 中:

render() {
  // React 加载一个新 div,并在其中渲染子组件
  return (
    <div>
      {this.props.children}
    </div>
  );
}

有时候需要将子组件插入到 DOM 的其他位置:

render() {
  // React 不创建一个新 div。它将子组件渲染到`domNode`中。
  // `domNode` 是一个合法的 DOM 节点,不管它在 DOM 中处于什么位置。
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

Portal 的一个典型用例是这样的:当父组件带有 overflow:hidden 或 z-index 样式时,你希望子组件在视觉上能够“突破”它的容器。例如,对话框、悬停卡和工具提示。

点击链接查看演示:

https://codepen.io/gaearon/pen/yzMaBd

自定义 DOM 属性

React15 会忽略任何未知的 DOM 属性。React 会跳过它们,因为无法识别它们。

// 你的代码:
<div mycustomattribute="something" />

React 15 将渲染一个空的 div:

// React 15 的输出:
<div />

React16 的输出如下(自定义属性会被显示出来,根本不会被忽略):

// React 16 的输出:
<div mycustomattribute="something" />
在 state 中设置 null 避免重新渲染

在 React16 中,你可以直接在 setState() 中防止 state 更新和重新渲染。你只需要让你的函数返回 null。

const MAX_PIZZAS = 20;

function addAnotherPizza(state, props) {
  // 如果我有足够的披萨,就停止更新和重新渲染。
  if (state.pizza === MAX_PIZZAS) {
    return null;
  }

  // 否则的话,让披萨来得更猛烈些吧:D
  return {
    pizza: state.pizza + 1,
  }
}

this.setState(addAnotherPizza);

更多相关信息请阅读:

https://x-team.com/blog/react-render-setstate/

创建 ref

在 React16 中创建 ref 要容易得多。为什么需要使用 ref:

  • 管理焦点、文本选择或媒体播放。

  • 触发动画。

  • 与第三方 DOM 库集成。

ref 是使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。ref 通常是在构造组件时被分配给实例的属性,以便在整个组件中引用它们。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

ref 被传给元素后,可以通过 ref 的 current 属性访问对节点的引用。

const node = this.myRef.current;

ref 的值因节点的类型不同而有所不同:

当 ref 属性用于 HTML 元素时,在构造函数中使用 React.createRef() 创建的 ref 将底层 DOM 元素作为 current 属性。当 ref 属性用于自定义类组件时,ref 对象将已挂载的组件实例作为 current 属性。你可能不会在功能组件上使用 ref 属性,因为它们没有实例。

Context API

Context 提供了一种通过组件树传递数据的方法,无需在每一层手动传递 prop。

  React.createContext

const {Provider, Consumer} = React.createContext(defaultValue);

创建{Provider,Consumer}对。当 React 渲染 Consumer 时,它将从树中最接近的 Provider 读取当前上下文值。

defaultValue 参数只在消费者在树中找不到匹配的 Provider 时才会用到,这在单独测试组件时十分有用。注意:将 undefined 作为 Provider 值传递进去并不会导致 Consumer 使用 defaultValue。

  Provider
<Provider value={/* 一些值 */}>

一个允许 Consumer 订阅上下文变更的 React 组件。

一个 Provider 可以连接多个 Consumer,可以在树中嵌套 Provider,实现更深的值覆盖。

  Consumer
<Consumer>
  {value => /* 基于上下文值渲染一些东西 */}
</Consumer>

一个订阅上下文变更的 React 组件。

需要一个函数作为子组件。这个函数接收当前上下文值,并返回一个 React 节点。传给函数的 value 参数将等于树中最近的 Provider 的 value。如果没有匹配的 Provider,则 value 参数将等于传给 createContext() 的 defaultValue。

静态 getDerivedStateFromProps()

getDerivedStateFromProps 会在调用 render 方法之前被调用,它应该返回一个用于更新状态的对象,或者如果不更新任何状态就返回 null。

这个方法适用于一些罕见的用例,其中 state 依赖 prop 的变化。例如,可以很方便地实现一个 组件,它会比较上一个和下一个子组件,然后决定它们中的哪个需要进行动画渲染。

衍生 state 会导致冗长的代码,并让你的组件难以开发和维护。

你可以考虑更简单的替代方案:

如果你需要在 prop 发生变更时做一些其他事情(例如数据提取或动画),请改用 componentDidUpdate。如果你只想在 prop 发生变更时重新计算某些数据,请改用备忘辅助器:

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#what-about-memoization

如果你想在 prop 发生变更时“重置”某个状态,请考虑创建完全控制组件或带有键的完全不受控制组件。

这个方法无法访问到组件实例。如果你愿意,可以在类定义之外提取组件 prop 和 state 的纯函数,在 getDerivedStateFromProps() 和其他类方法之间重用一些代码。

请注意,不管怎样,这个方法都会在每次进行渲染时触发。这与 UNSAFE_componentWillReceiveProps 完全相反。它只在父组件进行重新渲染时触发,而且不作为本地 setState 的结果。

我们将 nextProps.someValue 与 this.props.someValue 进行对比。如果它们的值不一样,我们就执行一些操作:

static getDerivedStateFromProps(nextProps, prevState){   if(nextProps.someValue!==prevState.someValue){     
   return { someState: nextProps.someValue};  
} else return null;}

它接收两个参数 nextProps 和 prevState。如前所述,你无法在这个方法中访问 this。你必须将 prop 存储在 state 中,然后将 nextProps 与之前的 prop 进行对比。在上面的代码中,nextProps 和 prevState 进行了比较。如果两者不同,则返回一个用于更新状态的对象,否则就返回 null,表示不需要更新状态。如果 state 发生变更,就会调用 componentDidUpdate,我们可以像在 componentWillReceiveProps 中那样执行所需的操作。

额外奖励:React 生命周期事件

  英文原文

https://medium.freecodecamp.org/why-react16-is-a-blessing-to-react-developers-31433bfc210a

  课程推荐

双节来临,准备好反超那些高级程序员了吗?别顾着欢脱,学习直上才是硬道理。20 余类硬技能,365 天一线技术课程任性看,对技术的任何需求,这里都可以给你解答。


 
前端之巅 更多文章 你拆分JS代码的方法可能是错的! 阿里是如何让iOS 12越狱成功的? 多端统一开发框架 Taro 1.0 正式发布,全面支持小程序 15行代码让苹果设备崩溃,最新的iOS 12也无法幸免 选择JavaScript开源库时,你需要考虑这些问题
猜您喜欢 黑客走开系列1:Python使用元组做函数实参让代码更安全! 硬盘坏道修复全攻略 35 个 Java 代码性能优化总结 图解:支付宝钱包系统架构剖析 大屏展示OneOaaS Monitor社区版发布