React 16.10.0 发布,构建用户界面的 JavaScript 库

by admin on 2020年2月10日

React 16.10.0 发布了,更新内容如下:

概述

目前,我需要在应用界面上实时显示日期(精确到秒),该如何处理?
按照React的规则,传递给组件的props对象只能读,而实时日期是实时更新的,那就只有实时的调用组件传递不同的props值,当然,这可以通过设置一个定时器实现,定时调用ReactDOM.render(),传递不同的参数给props来改变输出。如果只是这样,那用JavaScrip就能实现,并不能体现React的简洁与高效。

澳门新葡亰信誉平台游戏 1

React DOM

  • 修复了未记录挂钩更新的边缘情况
    (@sebmarkbage in #16359)
  • 修复 heuristic
    (@sebmarkbage in #16739)
  • 卸载期间清除其他光纤字段以节省内存
    (@trueadm in #16807)
  • 修复 Firefox 中必填文本字段的错误
    (@halvves澳门新葡亰信誉平台游戏 , in #16578)
  • 如果可用,选择 Object.is 而不是内联 polyfill
    (@ku8ar in #16212)
  • 修复混合挂起和错误处理时的错误
    (@acdlite in #16801)

定时器实现

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toString('yyyy-MM-dd HH:mm:ss')}</h2>
    </div>
 );
  ReactDOM.render(
    element,
    document.getElementById('root')
 );
}
setInterval(tick, 1000);

每隔1s调用一次tick方法,获取改变后的元素,重新渲染。
如果仅仅这样的话,组件是没法重用的。
现将<h1>Hello, world!</h1>和<h2>It is {new
Date().toString(‘yyyy-MM-dd
HH:mm:ss’)}</h2>分别抽象成一个展示欢迎的函数组件和一个展示时钟的组件:

function Welcome(props) {
    return <h1>hello, {props.name}</h1>;
}
function Clock(props){
    return <h2>It is {props.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
}
function tick() {
  const element = (
    <div>
      <Welcome name= "world" />
      <Clock date={new Date()} />
    </div>
 );
  ReactDOM.render(
    element,
    document.getElementById('root')
 );
}
setInterval(tick, 1000);

从上可以看到,Welcome和Clock组件得到了复用。但是还不够好,因为:
1.ReactDOM.render()每秒都会执行一次,其实我只希望它执行一次,因为真正需要改变的是Clock组件;
2.Welcome组件每秒都会执行一次,但是它的内容无任何变化;
真正需要的是ReactDOM.render()只执行一次,时钟的变化交由Clock组件自身完成。

React 开发者,同时也是 Redux 和 Create React App 作者之一的 

Scheduler(实验性)

  • 通过将内部数据结构切换为最小二进制堆来提高队列性能 (@acdlite in #16245)
  • 使用间隔较短的 postMessage 循环,而不是尝试使用
    requestAnimationFrame
    对齐框架边界 (@acdlite in #16214)

state实现

Dan 表示,目前 React DOM
存在着相当多的已知问题,如果没有更大的内部变化,其中一些问题很难或根本无法修复,这些问题导致了无数的后续修复工作并产生了大量的技术债务。在这项被称为
React Fire 的现代化 Reat DOM
改造计划中,开发团队希望删除事件系统中的一些抽象,这些抽象从 React
诞生以后就几乎没有被触及,并且是系统复杂性和项目包变大的根源。

useSubscription

  • 当发生突变并且先前的更新仍在进行时,避免 tearing issue
    (@bvaughn in #16623)

更新说明及下载地址

React 16.10.1 修复了一个 bug:

1.将Clock函数组件改成类组件

你可以通过5个步骤将函数组件 Clock 转换为类
==创建一个名称扩展为 React.Component 的ES6 类
==创建一个叫做render()的空方法
==将函数体移动到 render() 方法中
==在 render() 方法中,使用 this.props 替换 props
==删除剩余的空函数声明

class Clock extends Component{
    render(){
        return <h2>It is {this.props.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
   }
}

Dan 详细介绍了目前关于 React Fire 的一些想法:

React DOM

  • 修复 Next.js 应用程序中的回归
    (@sebmarkbage in #16943)

更新说明及下载地址

(文/开源中国)    

2.给Clock类添加局部状态

在 render() 方法中使用 this.state.date 替代 this.props.date

class Clock extends Component{
    render(){
        return <h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
   }
}
  • 停止在 value 属性中映射输入值。这最初在 React 15.2.0
    中被添加,它是一个非常普遍的要求,因为人们对 DOM 的概念模型是他们在
    DOM 检查器中看到的值应该与 JSX 的属性匹配。但这并不是 DOM
    的工作方式。当你在键入字段时,浏览器不会更新 value 属性,那么 React
    也不应该这样做。事实证明,这种变化虽然可能对依赖 CSS
    选择器的一些代码有所帮助,但却引发了一系列
    bug,其中一些仍未得到修复。

3.添加一个类构造函数来初始化状态 this.state

class Clock extends Component{
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
   }

    render(){
        return <h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
   }
}

注意我们如何传递 props 到基础构造函数的:

constructor(props) {
        super(props);
        this.state = {date: new Date()};
   }

类组件应始终使用props调用基础构造函数。

4.将定时器移到Clock组件本身

class Clock extends Component{
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
   }​
    tick() {
        this.setState({
            date: new Date()
       });
   }
    render(){
        return <h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
   }
}
  • 在 React 根目录而不是 document 对象中添加事件。将 React
    应用程序嵌入到更大的系统中时,将事件处理程序添加到 document
    对象会成为一个问题。Atom
    编辑器是最早遇到这种情况的案例之一。任何大型网站最终也会发展出与
    stopPropagation 相关的非常复杂的边缘案例,它们与非 React 代码或跨
    React 进行交互。

5.从 <Clock /> 元素移除 date 属性

const element = (
    <div>
        <Welcome name= "world" />
        <Clock />
    </div>
);
ReactDOM.render(
    element,
    document.getElementById('root')
);

6.给组件添加生命周期函数

class Clock extends Component{
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
   }
    tick() {
        this.setState({
            date: new Date()
       });
   }
    componentDidMount(){
        //
   }
    componentWillUnmount(){
        //
   }
    render(){
        return <h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
   }
}

这些生命周期函数称作生命周期钩子。
当组件输出到 DOM 后会执行 componentDidMount()
钩子,即组件渲染到DOM后执行,此处一般用来加载数据的,但只执行一次,可以把定时器设置在此处:

componentDidMount(){
        // 装载定时器
        this.timerID = setInterval(
           () => this.tick(),
            1000
       );
   }

每秒执行一次tick,而tick方法则是通过this.setState更改局部状态date。
保存定时器ID,卸载时用到。
this.props由React本身设置,this.state具有特殊的含义,但如果需要存储不用于视觉输出的东西,则可以手动向类中添加其他字段。
如果你不在render()中使用某些东西,它就不应该在状态中。//
理论上是这样,但有时为了控制状态,也可以定义一些不用在render中使用的字段。
我们将在 componentWillUnmount()生命周期钩子中卸载计时器:

componentWillUnmount(){
        // 卸载定时器
        clearInterval(this.timerID);
   }

看看完整的代码:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
function Welcome(props) {
    return <h1>hello, {props.name}</h1>;
}

class Clock extends Component{
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
   }
    tick() {
        this.setState({
            date: new Date()
       });
   }
    componentDidMount(){
        // 装载定时器
        this.timerID = setInterval(
           () => this.tick(),
            1000
       );
   }
    componentWillUnmount(){
        // 卸载定时器
        clearInterval(this.timerID);
   }

    render(){
        return <h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
   }
}

const element = (
    <div>
        <Welcome name= "world" />
        <Clock />
    </div>
);
ReactDOM.render(
    element,
    document.getElementById('root')
);

此时,你可以看到浏览器每秒更新一次时钟。是不是很酷很神奇?
那这个过程是怎么实现的呢?
1.当<Clock
/>被传递给ReactDOM.render()时,React调用Clock组件的构造函数,由于Clock需要显示当前时间,所以使用包含当前时间的对象来初始化this.state。
2.React调用Clock组件的render()方法渲染屏幕,然后React更新DOM以匹配Clock的渲染输出。
3.当Clock的输出插入到DOM中时,React调用componentDidMount()生命周期钩子。在其中,Clock组件要求浏览器设置一个定时器,每秒钟调用一次tick()。
4.浏览器每秒钟调用tick()方法。
在其中,Clock组件通过使用包含当前时间的对象调用setState()来调度UI更新。
通过调用setState(),React
知道状态已经改变,并再次调用render()方法再次渲染屏幕。
而这次,render()方法中的this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新DOM。//
你会发现并不会再调用componentDidMount,因为该函数只在第一次装载的时候调用。
5.一旦Clock组件被从DOM中移除,React会调用componentWillUnmount()这个钩子函数,定时器也就会被清除。
从上可知Clock组件中3个方法的执行顺序:
constructor 组件调用时
render 组件调用时,state变化后调用
componentDidMount 组件装载后
componentWillUnmount 组件卸载后

  • 从 onChange 迁移到 onInput,并且不要填充不受控制的组件。React 在
    DOM
    中使用不同的事件名称来表示输入事件这让人十分困惑。虽然我们通常避免在没有显著优势的情况下进行这样的大改动,但在这种情况下,还是希望去消除一些复杂性,这些复杂性仅对改变控制输入等边缘情况是必需的。因此,将这两个更改结合在一起是有意义的,并将其用作为一种 onInput
    和 onChange 完全按照 DOM 事件对不受控制的组件执行的操作的尝试。

正确地使用状态

状态(state)很灵活,也很实用,但使用时需要注意,不然,很有可能得不到想要的结果。
1.不要直接更新状态

this.state.date = new Date();

此时你可以看到,时钟并不会更新。
应该使用this.setState()函数。
构造函数是唯一能够初始化this.state的地方。
2.状态更新可能是异步的
React 可以将多个setState() 调用合并成一个调用来提高性能。
因为 this.props 和 this.state
可能是异步更新的,你不应该依靠它们的值来计算下一个状态。
例如,此代码可能无法更新计数器:

this.setState({
    counter: this.state.counter + this.props.increment,
});

要修复它,请使用第二种形式的 setState() 来接受一个函数而不是一个对象。
该函数将接收先前的状态作为第一个参数,将需要更新的值作为第二个参数:

this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
}));

上方代码使用了箭头函数,但它也适用于常规函数:

this.setState(function(prevState, props) {
  return {
        counter: prevState.counter + props.increment
 };
});

箭头函数后续会说到。
3.状态更新合并
当你调用 setState() 时,React 将你提供的对象合并到当前状态。
你可以调用 setState() 独立地更新它们。
数据自顶向下流动
父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。
这就是为什么状态通常被称为局部或封装。
除了拥有并设置它的组件外,其它组件不可访问。
组件可以选择将其状态作为属性传递给其子组件:

<h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>

这也适用于用户定义的组件:

<FormattedDate date={this.state.date} />

FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock
状态、还是来自 Clock 的属性、亦或手工输入:

function FormattedDate(props) {
  return <h2>It is {this.state.date.toString('yyyy-MM-dd HH:mm:ss')}</h2>;
}

这通常被称为自顶向下或单向数据流。
任何组件状态由组件本身所有,并且只能传递到树中下方的组件。
为了表明所有组件都是真正隔离的,我们可以在element元素中同时渲染三个Clock:

const element = (
    <div>
        <Welcome name= "world" />
        <Clock />
        <Clock />
        <Clock />
    </div>
);

ReactDOM.render(
    element,
    document.getElementById('root')
);

每个 Clock 建立自己的定时器并且独立更新。
在React应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。可以在有状态组件中使用无状态组件,反之亦然。

注:
本教程相关的所以源码,可在https://github.com/areawen2GHub/reacttest.git下载

参考地址:
https://react.bootcss.com/react/docs/state-and-lifecycle.html

  • 大大简化事件系统。自 2013
    年实现以来,当前事件系统几乎没有变化。它在 React DOM 和 React Native
    中被重复使用,因此它是不必要的抽象。它提供的许多 polyfills
    对于现代浏览器来说是不必要的,并且其中一些会产生比他们解决的问题更多的问题。它也占据 React
    DOM
    包的很大一部分。关于这一点,目前还没有非常具体的计划,但是可能会完全将事件系统再 fork
    一份出来,然后看看在更接近 DOM
    赋予的东西的时候可以去做些什么。而完全摆脱冒泡事件似乎是合理的。应该停止冒泡活动,比如媒体事件,这些事件不会在
    DOM 中冒泡,也没有充分的理由冒泡。

  • className → class。这已经被提出了无数次。目前已经允许在 React 16
    中将 class 传递到 DOM
    节点中,这样做导致的混乱实际上是比语法限制来得有意义的。

Dan
还表示,为了达成此目标,可能需要降低与某些旧浏览器的兼容性,并且可能需要更多独立的
polyfill,详情可以查看原文进行了解。

对于这个 issue,你是怎么看待的呢?欢迎在评论中留下你的想法。

(文/开源中国)    

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图