错误边界和render props

部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界。

术语 render prop 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术

这里简单学习一下这两个的学习和使用场景。

Error Boundaries

错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

如果一个 class 组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 你同样可以将错误日志上报给服务器
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

注意错误边界仅可以捕获其子组件的错误,它无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 catch {} 的工作机制。

自 React 16 起,任何未被错误边界捕获的错误将会导致整个 React 组件树被卸载。

错误边界无法捕获事件处理器内部的错误。React 不需要错误边界来捕获事件处理器中的错误。与 render 方法和生命周期方法不同,事件处理器不会在渲染期间触发。因此,如果它们抛出异常,React 仍然能够知道需要在屏幕上显示什么。

如果你需要在事件处理器内部捕获错误,使用普通的 JavaScript try / catch 语句

Render Props

具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。

<DataProvider render={data => (
    <h1>Hello {data.target}</h1>
  )}
/>

看一个render prop的实例:

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <div
        src="cat.jpg"
      >
          'position': 'absolute', 'left': {mouse.x}, 'top': {mouse.y},
      </div>
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>

        {/*
          Instead of providing a static representation of what <Mouse> renders,
          use the `render` prop to dynamically determine what to render.
        */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>移动鼠标!</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

重要的是要记住,render prop 是因为模式才被称为 render prop ,你不一定要用名为 render 的 prop 来使用这种模式。事实上, 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”.

尽管之前的例子使用了 render,我们也可以简单地使用 children prop!

<Mouse children={mouse => (
  <p>鼠标的位置是 {mouse.x},{mouse.y}</p>
)}/>
// 记住,children prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。相反,你可以直接放置到元素的内部!

<Mouse>
  {mouse => (
    <p>鼠标的位置是 {mouse.x},{mouse.y}</p>
  )}
</Mouse>

//由于这一技术的特殊性,当你在设计一个类似的 API 时,你或许会要直接地在你的 propTypes 里声明 children 的类型应为一个函数。

Mouse.propTypes = {
  children: PropTypes.func.isRequired
};

如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 React.PureComponent 带来的优势。因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值。所以可以考虑将prop定义成实例的的方法。

results matching ""

    No results matching ""