# react基本用法

# 路由传递参数

1.问号传参 基于location来完成处理let {data, location: {search}} = this.props

<Route path='/frame' component={Frame} />
<NavLink  to='/frame?name=yydbb' >    

2.地址栏传参基于match的params来完成处理let {data, match: {params}} = this.prop

<Route path='/home/:id' component={Home} />
<NavLink  to='/home/1' >     
//props取值
props.match.params.id

3.地址栏不可见,刷新不存在let {data, location: {state}} = this.props
state 传参的方式只支持Browserrouter路由,不支持hashrouter

<Route path='/note' component={Note} />
<NavLink className="right-link" to={{pathname:'/note',state:{id:12}}}  />           

4.地址栏不可见,query传参(刷新页面后参数消失)

# 发起 AJAX 请求

应当将AJAX 请求放到componentDidMount函数中执行

  • componentsDidMount里面请求数据此时dom已经渲染上去,从用户友好角度来讲,我们更愿意让用户先看到一个没有数据的方式,再通过一个spin的动画,来加载数据;componentsWillMount里面请求数据,拿到数据之后setState的时机是不确定的,可能是render之前,也可能是render之后,并不是下一个时间段,这依赖于ajax的返回时间,所以不能准时的出现loading图,所以说会出现较长白屏的现象.
  • React16 调和算法Fiber会通过开始或停止渲染的方式优化应用性能,其会影响到componentWillMount的触发次数。对于 componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用 componentWillMount

# PropTypes

JavaScript 是弱类型语言,所以请尽量声明 propTypes 对 props 进行校验,以减少不必要的问题。

  • PropTypes.array
  • PropTypes.bool
  • PropTypes.func
  • PropTypes.number
  • PropTypes.object
  • PropTypes.string
  • PropTypes.any
  • PropTypes.shape
import PropTypes from "prop-types"

static defaultProps = {
    a: 0,
    b: 0,
};
static propTypes = {
    a: PropTypes.number,
    b: PropTypes.number,
    store: PropTypes.shape({
           subscribe: PropTypes.func.isRequired,
           dispatch: PropTypes.func.isRequired,
           getState: PropTypes.func.isRequired
    }),
    data: PropTypes.array.isRequired//必须有
};

参考链接

# children

通过 JSX 嵌套,将任意组件作为子组件传递给它们

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
    </FancyBorder>
  );
}

# 缓存路由

参考文章

# 获取实际的DOM

ReactDOM.findDOMNode(this.sildeWrapper).clientWidth;   //废弃  改用ref

# 添加DOM

<div dangerouslySetInnerHTML={{__html: '<p>123</p>'}} />

# createPortal

Portals提供了一种脱离#app的组件。
适用 toast,通知,警告

//第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。
//第二个参数(container)/是一个 DOM 元素。
ReactDOM.createPortal(child, container)

# StrictMode

开发模式会调用多次

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告

import React from "react";
function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}

StrictMode 目前有助于:

  • 识别不安全的生命周期
  • 关于使用过时字符串 ref API 的警告(React.createRef();)
  • 关于使用废弃的 findDOMNode 方法的警告(貌似没有)
  • 检测遗留 context API
  • 检测意外的副作用

react 在检测意外的副作用时可能重复调用某些生命周期方法、hooks 或者 render 方法,其包括:

class 组件的 constructor,render 以及 shouldComponentUpdate 方法 class 组件的生命周期方法 getDerivedStateFromProps 函数组件 状态更新函数 (即 setState 的第一个参数) 函数组件中 useState,useMemo 或者 useReducer 中的函数 当 React 发现在重复调用这些方法时出现了内存泄漏、无限循环或者其他奇怪的表现时会在控制台输出错误,以供程序员快速定位错误。

简单来说就是我们在使用hooks或者在某些生命周期函数中不应该使用有副作用的代码。在开发模式的 StrictMode 下,React 会帮助我们发现这些不好的代码并给予提示。

StrictMode 的这个重复调用的特性只使用于开发模式,在生产模式下不会触发多次调用

# Error Boundaries

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

static getDerivedStateFromError(error)

当后代组件抛出错误时,首先会调用这个方法,并将抛出的错误作为参数。无论这个方法返回什么值,都将用于更新组件的状态。

在后代组件抛出错误之后,也会调用componentDidCatch方法除了抛出的错误之外,还会有另一个参数,这个参数包含了有关错误的更多信息

  • getDerivedStateFromError是在reconciliation阶段触发,所以getDerivedStateFromError进行捕获错误后进行组件的状态变更,不允许出现副作用。
  • componentDidCatch因为在commit阶段,因此允许执行副作用。 它应该用于记录错误之类的情况

WARNING

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

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

只有 class 组件才可以成为错误边界组件
自React16起,任何未被错误边界捕获的错误将会导致整个React组件树被卸载。
错误边界的粒度由你来决定,可以将其包装在最顶层的路由组件并为用户展示一个 “Something went wrong” 的错误信息, 就像服务端框架经常处理崩溃一样。 你也可以将单独的部件包装在错误边界以保护应用其他部分不崩溃。

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; 
  }
}

# ref(非受控)

React的ref有4种用法:

  • 字符串(已废弃) 通过this.refs[refName]来引用真实的dom节点
<input ref="inputRef" /> //this.refs['inputRef']来访问
  • 回调函数 函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问
function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}
  • React.createRef()
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
  • Hooks useRef()
const Example = () => {
    let inputRef = useRef(null);
    useEffect(() => {
        inputRef.current.focus();
    }, []);
    return (
        <input type="text" ref={inputRef}/>
    )
};

# Code Splitting

import() 适用于函数

//add.js
export const sum = (a, b) => a + b;

//index.js
import React from 'react';
function Index() {
    const handleClick = async () => {
        const {sum} = await import('./add');
        console.log(sum(1, 2));
    };
    return (
        <button onClick={handleClick}>点击</button>
    );
}
export default Index;

React.lazy 适用于组件 目前不支持服务端渲染

const Foo = React.lazy(() => import('../components/Foo'));
render() {
    return (
            <Suspense fallback={<div>loading...</div>}>
                <Foo/>
            </Suspense>
        )
}

React.lazy 目前只支持默认导出(default exports)你可以创建一个中间模块,来重新导出为默认模块

//a.jsx
export const A = /* ... */;
export const B = /* ... */;
//middle.js
export { A as default } from "./a";   //A就相当于默认导出
//index.jsx
import React, { lazy } from 'react';
const A = lazy(() => import("./middle"));

<Suspense fallback={<div>loading...</div>}>
    <A/>
</Suspense>