再读React官方文档

再读React

笔者最近空闲时间比较多,这也可能是笔者这未来人生中时间最自由的一个月。所以想抓紧时间巩固和学习一下新知识,在公司实习期间一直在写业务,所以自己学习的时间很少,为了追上同事的步伐,一定要抓紧这一个月又一个质的突破。

这篇文章就是记录我再读文档的时候一些知识盲区

关于生命周期

组件挂载

当组件实例被创建,并插入到DOM中,其生命周期调用顺序为

  • constructor()
    • 通过设置this.state来初始化State
    • 为事件处理函数绑定实例 this.handleClick = this.handleClick.bind(this)
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

组件更新

  • static getDerivedStateFromProps()
  • shouldComponentUpdate():返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响(可使用PureComponent
  • render()
  • getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM中捕获一些信息(例如,滚动位置)
  • componentDidUpdate(prevProps, prevState, snapshot): 会在更新后立即调用,首次渲染不会调用。第三个参数是getSnapshotBeforeUpdate的返回值

关于API

setState

1
2
3
4
5
6
7
8
this.setState((state,props)=>{
return {counter: state.counter + props.step}
})

// 或
this.setState({
counter: this.state.counter + this.props.step;
})

forceUpdate

默认情况下当props或者states发生变化时,就会触发render()函数。当组件还依赖于其他变量时,可以使用forceUpdate()强制更新。

React.lazy

React.lazy函数能够让你像渲染常规组件一样处理动态引入的组件

用法如下

1
2
3
4
5
6
7
8
9
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}

React.Suspense

Suspense直译为中文就是悬念的意思。可以用React.Suspense组件为组件添加loading状态,以作优雅降级。

1
2
3
4
5
6
7
8
9
10
11
12
import {Suspense} from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
return (
<Suspense fallback={<div>'loading....'</div>}>
<div>
<OtherComponent />
</div>
</Suspense>
);
}

==注意:Lazy、Suspense暂不支持服务端渲染。==

React.memo

适用于函数式组件而不适用于class组件。类似于PureComponent,通过记忆组件渲染结果的方式来提高组件的性能表现。

1
2
3
const MyComponentMemo = React.memo(function MyComponents(props){
return <div> {props.name} </div>
})

默认情况下只会对复杂对象进行浅层对比, 可自定义比较函数来控制对比过程。

1
2
3
4
5
6
7
8
9
10
11
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(MyComponent, areEqual);

错误边界

错误边界是一种React组件,该组件可以捕获并打印发生在其子组件树任何位置的JavaScript错误,并且他会渲染出备用UI,而不是那些错误的子组件树。错误边界在渲染期间、生命周期方法和整个组件树中的方法捕获错误。但错误边界无法捕获下列情况

  • 事件处理(trycatch
  • 异步函数(如setTimeOutrequestAnimationFrame的回调函数)
  • 服务端渲染
  • 错误边界组件自身抛出的错误

如何使用

如果一个class定义了getDerivedStateFromError()componentDidCatch()这两个生命周期的一个或两个,它就变成了一个错误边界组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}
}

Refs转发

ref自动的通过组件传递到其子组件。

在下面的示例中,FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button;

当ref挂载完成,将ref.current将指向ButtonDOM;

1
2
3
4
5
6
7
8
9
10
const FancyButton = React.forwradRef((props,ref)=>(
<Button ref={ref}>
{props.children}
</Button>
))

const ref = React.createRef();
return <FancyButton ref={ref}>
转发refs
</FancyButton>

在高阶组件中转发refs

  • 定义一个高阶组件LogProps,它的作用只是将组件的props输出到控制台;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function logProps(Component){

    class LogProps extends React.Component {
    componentDidUpdate(preProps){
    console.log("preProps: "+preProps);
    console.log("props: "+this.props);
    }
    render(){
    return <Component {...this.props} />
    }
    }

    return LogProps;
    }
  • 定义一个组件FancyButton。实际上导出的是LogProps这个高阶组件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class FancyButton extends React.Component {
    focus() {
    // ...
    }

    // ...
    }

    // 我们导出 LogProps,而不是 FancyButton。
    // 虽然它也会渲染一个 FancyButton。
    export default logProps(FancyButton);
  • 使用FancyButton组件。实际上ref是挂载到LogProps上的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import FancyButton from './FancyButton';

    const ref = React.createRef();

    // 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
    // 尽管渲染结果将是一样的,
    // 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
    // 这意味着我们不能调用例如 ref.current.focus() 这样的方法
    <FancyButton
    label="Click Me"
    handleClick={handleClick}
    ref={ref}
    />;
  • 转发refs。 修改一下logProps高阶组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function logProps(Component){

    class LogProps extends React.Component {
    componentDidUpdate(preProps){
    console.log("preProps: "+preProps);
    console.log("props: "+this.props);
    }
    render(){
    const {forwradRefs, ...rest} = this.props;
    return <Component refs={forwardRefs} {...rest} />
    }
    }

    return React.forwardRefs((props,refs)=>(
    // 给logProps组件传递一个forwardRefs的属性
    return <LogProps forwradRefs={refs} {...props} />
    ));
    }

注意: 高阶组件转发refs时,在React-DevTools调试中,可能会造成不便,可设置displayName来解决

在 DevTools 中显示自定义名称

深入JSX

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

  • 自定义``
  • JSX类型不能是一个表达式。但JSX类型可以是一个由大写字母开头的变量。

Portals

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

用法

1
2
3
4
5
// 把子元素渲染到domNode中。domNode是一个在任何位置的有效DOM节点。
return ReactDOM.createProtal
this.props.children,
domNode
)

Portals中的时间捕获与冒泡

使用portal,虽然会操作DOM树,但由于portal仍存在于React树,且与DOM树中的位置无关。所以portal中的事件,仍会冒泡到React树中的父组件。context等的效果也是跟普通的组件无异的。

在父组件里捕获一个来自 portal 冒泡上来的事件,使之能够在开发时具有不完全依赖于 portal 的更为灵活的抽象。例如,如果你在渲染一个 <Modal /> 组件,无论其是否采用 portal 实现,父组件都能够捕获其事件。

create-react-class

除了class创建一个组件,和函数式组件,还可以通过create-react-class来新建一个组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var createReactClass = require('create-react-class');

const Greeting = createReactClass({
// 生命周期
componentDidMount: ()=>{
...
}

// 初始化props
getDefaultProps: ()=>{
return {
name: 'Mary'
}
}

// 初始化state
getInitialState: ()=>{
return {

}
}

// 方法
handleClick: ()=>{
console.log("!!!!")
}

render:()=>{
return <div onClick={this.handleClick}>1234</div>
}
})
-------------本文结束感谢您的阅读-------------