React高级指引

Fragments

<React.Fragment>类似于vue 中的<template>,用来对子组件进行分组等操作,

有时,语义化的 HTML 会被破坏。比如当在 JSX 中使用 <div> 元素来实现 React 代码功能的时候,又或是在使用列表(<ol><ul><dl>)和 HTML <table> 时。 在这种情况下,我们应该使用 React Fragments 来组合各个组件。

将一个集合映射到一个 Fragments 数组 - 举个例子,创建一个描述列表:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // 没有`key`,React 会发出一个关键警告
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

引出

HTML

元素 (或 HTML 描述列表元素)是一个包含术语定义以及描述的列表,通常用于展示词汇表或者元数据 (键-值对列表)。

严格模式

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。

StrictMode 目前有助于:

在 CSS 中,如果你不希望节点成为布局的一部分,则可以使用 display: contents 属性。

display:contents示例

幂等,多次执行所产生的影响均与一次执行的影响相同

无障碍辅助功能

Accessibility,也被称为 a11y

for 在 JSX 中应该被写作 htmlFor

代码分割

Create React AppNext.jsGatsby

Webpack(代码分割)和 Browserify(factor-bundle)这类打包器支持的一项技术,能够创建多个包并在运行时动态加载。

react.lazy

React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 defalut export 的 React 组件。

Suspense

如果在父组件调用react.lazy加载子组件时,父组件加载完成子组件没有加载完成,可以调用Suspense组件进行优雅降级

你甚至可以用一个 Suspense 组件包裹多个懒加载组件

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}

如果模块加载失败(如网络问题),它会触发一个错误。你可以通过异常捕获边界(Error boundaries)技术来处理这些情况,以显示良好的用户体验并管理恢复事宜。

import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);

React.lazy 目前只支持默认导出(default exports)。如果你想被引入的模块使用命名导出(named exports),你可以创建一个中间模块,来重新导出为默认模块。这能保证 tree shaking 不会出错,并且不必引入不需要的组件。

错误边界

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

  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

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

注意

错误边界无法捕获以下场景中产生的错误:

  • 事件处理(了解更多
  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调函数)
  • 服务端渲染
  • 它自身抛出来的错误(并非它的子组件)

引出

命令式和声明式函数的区别

声明式专注于我们所要实现的结果,不用去思考具体的过程

服务端渲染

高阶组件

高阶组件是参数为组件,返回值为新组件的函数。

被包装组件接收来自容器组件的所有 prop,同时也接收一个新的用于 render 的 data prop。

HOC 不应该修改传入组件,而应该使用组合的方式,通过将组件包装在容器组件中实现功能

约定

  • 将不相关的props传递给被包裹的组件

  • 最大化可组合性

  • 包装显示名称以轻松调试,recompose 提供了这个函数

    function withSubscription(WrappedComponent) {
      class WithSubscription extends React.Component {/* ... */}
      WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
      return WithSubscription;
    }
    
    function getDisplayName(WrappedComponent) {
      return WrappedComponent.displayName || WrappedComponent.name || 'Component';
    }
    

注意事项

  • 不要在render方法中使用HOC,HOC每次返回不同的组件,会导致组件树卸载之前的组件并重新渲染新组件。

  • 务必复制静态方法

    // 定义静态函数
    WrappedComponent.staticMethod = function() {/*...*/}
    // 现在使用 HOC
    const EnhancedComponent = enhance(WrappedComponent);
    
    // 增强组件没有 staticMethod
    typeof EnhancedComponent.staticMethod === 'undefined' // true
    
  • refs不会被传递

引出

React高阶组件(HOC)模型理论与实践

深入理解 React 高阶组件

FLUX架构

React 的 controller view 模式

深入 JSX

JSX 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。

使用JSX的注意事项:

  • 由于 JSX 会编译为 React.createElement 调用形式,所以 React 库也必须包含在 JSX 代码作用域内。

    import React from 'react';
    import CustomButton from './CustomButton';
    
    function WarningButton() {
      // return React.createElement(CustomButton, {color: 'red'}, null);
      return <CustomButton color="red" />;
    }
    
  • 在 JSX 中,你也可以使用点语法来引用一个 React 组件。

    import React from 'react';
    
    const MyComponents = {
      DatePicker: function DatePicker(props) {
        return <div>Imagine a {props.color} datepicker here.</div>;
      }
    }
    
    function BlueDatePicker() {
      return <MyComponents.DatePicker color="blue" />;
    }
    
  • 用户定义的组件必须以大写字母开头,以小写字母开头的元素代表一个 HTML 内置组件

  • 不能将通用表达式作为 React 元素类型。如果你想通过通用表达式来(动态)决定元素类型,你需要首先将它赋值给大写字母开头的变量。

jsx中的props:

  • props的默认值为true

  • 以使用展开运算符 ... 来在 JSX 中传递整个 props 对象。你还可以选择只保留当前组件需要接收的 props,并使用展开运算符将其他 props 传递下去。

    const Button = props => {
      const { kind, ...other } = props;
      const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
      return <button className={className} {...other} />;
    };
    
    const App = () => {
      return (
        <div>
          <Button kind="primary" onClick={() => console.log("clicked!")}>
            Hello World!
          </Button>
        </div>
      );
    };
    
    

jsx中的子元素:

  • 包含在开始和结束标签之间的 JSX 表达式内容将作为特定属性 props.children 传递给外层组件。

  • 布尔类型、Null 以及 Undefined 将会忽略。这有助于依据特定条件来渲染其他的 React 元素。值得注意的是有一些 “falsy” 值,如数字 0,仍然会被 React 渲染。

    <div>
      {showHeader && <Header />}
      <Content />
    </div>
    
    

Optimizing Performance

设置shouldComponentUpdate

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

可以使用类似“浅比较”(对于基本数据类型,比较值,对于引用数据类型,比较引用)的模式来检查 propsstate 中所有的字段,以此来决定是否组件需要更新。继承 React.PureComponent

Portals

注意

对于模态对话框,通过遵循 WAI-ARIA 模态开发实践,来确保每个人都能够运用它。

当在使用 portal 时, 记住管理键盘焦点就变得尤为重要。

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

组件的挂载点虽然可以脱离父组件,但组件的事件通过冒泡机制仍可以传给父组件。

常用于对话框、悬浮卡以及提示框

Render Props

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

用于在组件之间共享功能

重要的是要记住,render prop 是因为模式才被称为 render prop ,你不一定要用名为 render的 prop 来使用这种模式。

注意

如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 React.PureComponent 带来的优势。

Context

Context 提供了一种在组件之间共享值的方式,而不必显式地通过组件树的逐层传递 props。

Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。

如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。

聊一聊我对 React Context 的理解以及应用

posted @ 2019-05-31 15:25  CodingSherlock  阅读(499)  评论(0编辑  收藏  举报