e

各种各样的Class components

Class Components

【类组件】

在本课程中,我们只使用了被定义为 Javascript 函数的 React 组件。 如果没有 React 16.8版本的 hook 功能,这是不可能的。 以前,在定义一个使用状态的组件时,必须使用 Javascript 的Class语法来定义它。

至少在一定程度上熟悉类组件是有益的,因为这个世界包含了很多旧的 React 代码,这些代码可能永远不会使用更新的语法完全重写。

让我们通过生成另一个非常熟悉的八卦应用来了解类组件的主要特性。 我们使用json-server 将八卦存储在文件db.json 中。 文件的内容是从这里提取的。

类组件的初始版本如下所示

import React from 'react'

class App extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <div>
        <h1>anecdote of the day</h1>
      </div>
    )
  }
}

export default App

这个组件现在有一个constructor ,其中目前没有任何事情发生,并且包含方法render。 正如人们猜测的那样,render 定义了如何以及什么会被渲染到屏幕上。

让我们为八卦列表和当前可见的八卦定义一个状态。 与使用useState-hook 相反,类组件只包含一个状态。 因此,如果状态是由多个“部分”组成的,那么它们应该作为状态的属性存储。 在构造函数中初始化状态:

class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = {      anecdotes: [],      current: 0    }  }

  render() {
    if (this.state.anecdotes.length === 0 ) {      return <div>no anecdotes...</div>
    }

    return (
      <div>
        <h1>anecdote of the day</h1>
        <div>
          {this.state.anecdotes[this.state.current].content}        </div>
        <button>next</button>
      </div>
    )
  }
}

组件状态位于实例变量的 this.state 中。 状态是具有两个属性的对象。this.state.anecdotes 是八卦列表,this.state.current 是当前显示八卦的索引。

在 函数化组件中,从服务器中获取数据的正确位置是在effect hook中,当一个组件渲染时执行,或者在必要的情况下降低频率,例如只在与第一次渲染结合时执行。

类组件的生命周期方法提供了相应的功能。 触发从服务器获取数据的正确位置在 声明周期方法 componentDidMount中,该方法在组件第一次渲染之后执行一次:

class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      anecdotes: [],
      current: 0
    }
  }

  componentDidMount = () => {    axios.get('http://localhost:3001/anecdotes').then(response => {      this.setState({ anecdotes: response.data })    })  }
  // ...
}

Http 请求的回调函数使用方法setState更新组件状态。 该方法只接触在作为参数传递给该方法的对象中定义的键。 键current 的值保持不变。

调用 setState 方法总是触发类组件的重新运行,即调用方法 render。

我们将用更改所显示的八卦的能力来结束组件。 下面是整个组件的代码,并突出显示了添加部分:

class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      anecdotes: [],
      current: 0
    }
  }

  componentDidMount = () => {
    axios.get('http://localhost:3001/anecdotes').then(response => {
      this.setState({ anecdotes: response.data })
    })
  }

  handleClick = () => {    const current = Math.floor(      Math.random() * (this.state.anecdotes.length - 1)    )    this.setState({ current })  }
  render() {
    if (this.state.anecdotes.length === 0 ) {
      return <div>no anecdotes...</div>
    }

    return (
      <div>
        <h1>anecdote of the day</h1>
        <div>{this.state.anecdotes[this.state.current].content}</div>
        <button onClick={this.handleClick}>next</button>      </div>
    )
  }
}

为了便于比较,这里的应用与一个函数组件是相同的:

const App = () => {
  const [anecdotes, setAnecdotes] = useState([])
  const [current, setCurrent] = useState(0)

  useEffect(() =>{
    axios.get('http://localhost:3001/anecdotes').then(response => {
      setAnecdotes(response.data)
    })
  },[])

  const handleClick = () => {
    setCurrent(Math.round(Math.random() * (anecdotes.length - 1)))
  }

  if (anecdotes.length === 0) {
    return <div>no anecdotes...</div>
  }

  return (
    <div>
      <h1>anecdote of the day</h1>
      <div>{anecdotes[current].content}</div>
      <button onClick={handleClick}>next</button>
    </div>
  )
}

在我们的例子中,差异是微小的。 函数式组件和类组件最大的区别在于,类组件的状态是一个单独的对象,并且使用 setState 方法更新状态,而在函数式组件中,状态可以由多个不同的变量组成,所有这些变量都有自己的更新函数。

在一些更高级的用例中,与类组件的生命周期方法相比,effect hook 提供了更好的控制副作用的机制。

使用 函数式组件的一个显著好处是不必处理 Javascript 类的 this 引用的自引用。

在我看来,以及其他许多人的看法中,类组件基本上没有比通过Hook增强的函数组件提供任何好处,除了所谓的错误边界机制,它目前(2020年2月16日)还没有被函数组件使用。

在编写新代码时,如果项目使用的是 React 16.8或更高,那么没有理由使用类组件。 另一方面,目前没有必要重写所有旧的React代码作为函数组件。

Organization of code in React application

【在 React application 中代码的组织】

在大多数应用中,我们遵循的原则是,将组件放在目录components 中,reducer程序放在目录reducers 中,负责与服务器通信的代码放在目录services 中。 这种组织方式适合于较小的应用,但是随着组件数量的增加,需要更好的解决方案。 组织一个项目没有一种正确的方法。 这篇文章100% 正确的方式构建一个 React 应用(或为什么根本没这回事)提供了一些关于这个问题的观点。

Frontend and backend in the same repository

【前端和后端在同一个仓库】

在这个过程中,我们已经将前端和后端创建到单独的存储库中。 这是一个非常典型的方法。 然而,我们通过复制将绑定的前端代码复制到后端存储库中来完成部署。 一个可能更好的方法是单独部署前端代码。 特别是使用 create-react-app 创建的应用,它非常简单,这要归功于内置的buildpack

有时可能会出现将整个应用放入单个存储库的情况。 在这种情况下,一种常见的方法是将package.jsonwebpack.config.js 放在根目录中,并将前端和后端代码放到它们自己的目录中,例如clientserver

此存储库为“单一存储库代码”的组织提供了一个可能的起点。

Changes on the server

【服务器上的更改】

如果服务器上的状态发生了变化,例如当其他用户将新博客添加到博客列表服务时,我们在本课程中实现的 React-frontend 将不会注意到这些变化,直到页面重新加载。 当前端触发后端中耗时的计算时,也会出现类似的情况。 如何将计算结果反映到前端?

一种方法是在前端执行轮询 ,这意味着重复对后端 API 的请求,例如使用setInterval-命令。

一个更复杂的方法是使用websocket ,利用它可以在浏览器和服务器之间建立一个双向通信通道。 在这种情况下,浏览器不需要轮询后端,而只需要为服务器使用 WebSocket 发送关于更新状态的数据的情况定义回调函数。

WebSockets 是由浏览器提供的 API,目前还不是所有的浏览器都支持它:

fullstack content

与直接使用 WebSocket API 不同,建议使用Socket.io-library,该库提供各种fallback-options,以防浏览器不完全支持 WebSocket。

第8章中,我们的议题是 GraphQL,它为后端数据发生更改时通知客户端提供了一个很好的机制。

Virtual DOM

【虚拟 DOM】

在讨论 React 时,经常会提到虚拟 DOM 的概念。 这到底是怎么回事? 正如在第0章中提到的那样,浏览器提供了一个DOM API ,浏览器中运行的 JavaScript 可以修改定义页面外观的元素。

当软件开发人员使用 React 时,他们很少或从未直接操作 DOM。 定义 React 组件的函数返回一组React-elements。 虽然有些元素看起来像普通的 html 元素。

const element = <h1>Hello, world</h1>

它们的核心也只是基于 JavaScript 的 React-elements。

定义应用组件外观的 React-elements 组成了Virtual DOM ,它在运行时存储在系统内存中。

ReactDOM库的帮助下,这些组件定义的虚拟 DOM 被渲染成一个真实的 DOM,浏览器可以使用 DOM API 显示这个 DOM:

ReactDOM.render(
  <App />,
  document.getElementById('root')
)

当应用的状态发生更改时,组件将定义一个新的虚拟 DOM。 React 在内存中使用以前版本的虚拟 DOM,而不是使用 DOM API 直接渲染新的虚拟 DOM React 计算更新 DOM 的最佳方式(删除、添加或修改 DOM 中的元素) ,使 DOM 反映新的虚拟 DOM。

On the role of React in applications

【React在应用中的作用】

在这些材料中,我们可能没有充分强调 React 主要是一个管理应用视图创建的库。 如果我们看看传统的模型视图控制器MVC-模式,那么 React 的领域将是View. React 的应用范围比较狭窄,例如Angular ,它是一个包含所有 Frontend MVN 框架的应用。因此 React 不被称为framework,而是一个库

在小型应用中,应用处理的数据存储在 React-components 的状态中,因此在这个场景中,组件的状态可以被认为是 mvc 架构的模型

但是,在讨论 React-applications 时通常不会提到 mvc 架构。 此外,如果我们正在使用 Redux,那么应用遵循Flux-架构,React 的角色更专注于创建视图。 应用的业务逻辑使用 Redux 状态和操作创建者来处理。 如果在 redux 应用中使用第6章熟悉的redux thunk,那么业务逻辑几乎可以与 React 代码完全分离。

因为 React 和Flux都是在 Facebook 上创建的,可以说只把 React 用作 UI 库是预期的用例。 遵循 flux 架构会给应用增加一些开销,如果我们讨论的是一个小型应用或原型,那么“错误地”使用 React可能是一个好主意,因为过度设计很少会产生最佳结果。

正如我在第6章的结尾所提到的,React Context-api为集中式状态管理提供了一种替代方案,无需 redux 之类的第三方库。 你可以阅读更多关于这个主题的 这个网站这个网站

React/node-application security

到目前为止,我们还没有触及安全。 我们现在也没有太多的时间,但是幸运的是系里有一个 MOOC-course Securing Software来处理这个重要的话题。

不过,我们还是要看一下这门课程的一些具体内容。

开放 Web 应用安全项目,又称为OWASP ,每年发布一份 Web 应用中最常见安全风险的清单。 最近的名单可以在这里找到( https://owasp.org/www-project-top-ten/)。 每年都可以发现同样的风险。

在列表的顶部,我们发现injection,这意味着例如,在应用中使用表单发送的文本被解释为与软件开发人员预期的完全不同。 最著名的注射类型可能是sql 注入

例如,如果下面的 sql 查询将在一个易受攻击的应用中执行:

let query = "SELECT * FROM Users WHERE name = '" + userName + "';"

现在让我们假设一个恶意用户Arto Hellas 将它们的名称定义为


Arto Hell-as'; DROP TABLE Users; --

这样名称将包含一个单引号 ',它是一个 sql 字符串的开头和结尾字符。 作为执行这两个 sql 操作的结果,第二个操作将销毁数据库表Users

SELECT * FROM Users WHERE name = 'Arto Hell-as'; DROP TABLE Users; --'

Sql-injections 可以通过sanitizing输入来阻止,这将需要检查查询的参数不包含任何禁止的字符,在这里是单引号。 如果发现被禁止的字符,它们将被替换为安全的替代字符,即逃逸字符。

注射攻击在 NoSQL-databases 也是可行的。 然而,mongoose 通过sanitizing查询来阻止它们。 你可以在这里找到更多关于这个话题的讨论。

跨网站脚本攻击(XSS) 是一种可以将恶意 JavaScript 代码注入合法 web 应用的攻击。 恶意程式码会在受害者的浏览器中执行。 如果我们尝试将如下内容注入 notes 应用

<script>
  alert('Evil XSS attack')
</script>

代码不会被执行,只是在页面上渲染为文本:

fullstack content

因为 React 处理变量中的消毒数据。 一些版本的 React 已经很容易受到 xss 攻击的攻击。 当然,安全漏洞已经得到修补,但不能保证还会有更多漏洞。

使用库时需要保持警惕; 如果这些库有安全更新,最好在自己的应用中更新这些库。 Express 的安全更新可以在库文档中找到,Node 的安全更新可以在本博客中找到。

您可以使用该命令检查依赖项的最新情况

npm outdated --depth 0

去年对于第四章节练习的模型答案已经有不少过时的依赖:

fullstack content

可以通过更新文件package.json 并运行命令 npm install 来更新依赖关系。 但是,依赖关系的旧版本不一定是安全风险。

Npm audit命令可用于检查债务的安全性。 它将应用中的依赖的版本号与集中式错误数据库中包含已知安全威胁的依赖的版本号列表进行比较。

对去年课程第四章节的练习进行 npm 审计时,打印一个长长的产生警告和修改建议列表。

下面是报告的一部分:

$ bloglist-backend npm audit

                       === npm audit security report ===

# Run  npm install --save-dev jest@25.1.0  to resolve 62 vulnerabilities
SEMVER WARNING: Recommended action is a potentially breaking change
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ braces                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ jest [dev]                                                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ jest > jest-cli > jest-config > babel-jest >                 │
│               │ babel-plugin-istanbul > test-exclude > micromatch > braces   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/786                             │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ braces                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ jest [dev]                                                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ jest > jest-cli > jest-runner > jest-config > babel-jest >   │
│               │ babel-plugin-istanbul > test-exclude > micromatch > braces   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/786                             │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ braces                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ jest [dev]                                                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ jest > jest-cli > jest-runner > jest-runtime > jest-config > │
│               │ babel-jest > babel-plugin-istanbul > test-exclude >          │
│               │ micromatch > braces                                          │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/786                             │
└───────────────┴──────────────────────────────────────────────────────────────┘

...


found 416 vulnerabilities (65 low, 2 moderate, 348 high, 1 critical) in 20047 scanned packages
  run `npm audit fix` to fix 354 of them.
  62 vulnerabilities require semver-major dependency updates.

仅仅一年之后,代码就充满了小的安全威胁。幸运的是,只有一个关键的威胁。

让我们运行 npm 审计修复程序,就像报告中建议的那样:

$ bloglist-backend npm audit fix

+ mongoose@5.9.1
added 19 packages from 8 contributors, removed 8 packages and updated 15 packages in 7.325s
fixed 354 of 416 vulnerabilities in 20047 scanned packages
  1 package update for 62 vulns involved breaking changes
  (use `npm audit fix --force` to install breaking changes; or refer to `npm audit` for steps to fix these manually)

62个威胁仍然存在,因为缺省情况下,如果它们的 版本号增加,审计修复程序不会更新依赖。

更新这些依赖可能导致整个应用崩溃。 剩下的威胁是由试探性的jest造成的。 安全版本为25.0.1,我们的应用是23.6.0版本。

因为jest是一个开发的依赖,所以威胁实际上是不存在的,但是为了安全起见,让我们更新一下:

npm install --save-dev jest@25.1.0 

更新后情况看起来不错

 $ blogs-backend npm audit

                       === npm audit security report ===

found 0 vulnerabilities
 in 1204443 scanned packages

在 OWASP 列表中提到的威胁之一是Broken Authentication 和相关的Broken Access Control。 如果应用用于流量加密的 https 协议,那么我们使用的基于令牌的身份验证是相当健壮的。 在实施访问控制时,不仅要检查浏览器中的用户身份,还要检查服务器上的用户身份。 糟糕的安全性是仅通过在浏览器代码中隐藏执行选项来阻止某些操作。

在 Mozilla 的 MDN 上有一个非常好的网站安全指南,这个 Website security -guide, 提出了一个非常重要的话题:

fullstack content

Express 的文档包括一个关于安全性的部分: 生产最佳实践: 安全性 ,这个部分值得一读。 还建议在后端添加一个名为Helmet的库。 它包括一组中间件,用于消除 Express 应用中的一些安全漏洞。

使用 ESlint 安全插件也是值得的。

Current trends

最后,让我们来看看未来的一些技术(或者实际上已经存在的技术) ,以及 Web 开发的方向。

Typed versions of JavaScript

有时候 JavaScript 变量的动态类型会产生令人讨厌的 bug。 在第5章节中,我们简要地讨论了PropTypes : 这是一种机制,可以对传递给 React-components 的props进行类型检查。

最近,人们对静态类型检查 static type checking的兴趣有了明显的提升,这种兴趣可以追溯到20世纪90年代。 目前最流行的 Javascript 类型版本是由 Microsoft 开发的Typescript。 将于4月底发布的第9章节的议题将是 Typescript。

Server side rendering, isomorphic applications and universal code

【服务器端渲染,同构应用和通用代码】

浏览器并不是唯一可以渲染使用 React 定义的组件的域。 渲染也可以在服务器上完成。 这种方法正在越来越多地被使用,例如,当服务器第一次访问应用时,服务器使用 React 生成的预渲染页面。 从这里开始,应用的操作继续像往常一样进行,这意味着浏览器执行 React,它操纵浏览器显示的 DOM。 在服务器上完成的渲染命名为:server side rendering

服务器端渲染的一个动机是搜索引擎优化。 搜索引擎一直以来都不擅长识别 JavaScript 渲染的内容,然而,这种趋势可能正在发生转变,例如,看看这个这个

当然,服务器端渲染并不是 React 或者甚至是 JavaScript 所特有的。 理论上,在整个堆栈中使用相同的编程语言可以简化概念的执行,因为可以在前端和后端运行相同的代码。

除了服务器端渲染之外,还有所谓的同构应用通用代码 的讨论,尽管对它们的定义还存在一些争议。 根据一些定义同构的 web 应用是同时在前端和后端执行渲染的应用。 另一方面,通用代码是可以在大多数环境中执行的代码,即前端和后端。

React 和 Node 为将同构应用实现为通用代码提供了一个理想的选择。

直接使用 React 编写通用代码目前仍然相当繁琐。 最近在 React 上实现了一个名为Next.js的库,这个库吸引了很多关注,是开发通用应用的一个很好的选择。

Progressive web apps

【渐进式网络应用】

最近人们开始使用 Google 推出的术语渐进式网络应用(PWA)。

简而言之,我们讨论的是 web 应用,尽可能在每个平台上利用这些平台中最好的部分。 移动设备的小屏幕不能妨碍应用的可用性。 PWAs 也应该在脱机模式下或缓慢的互联网连接下完美地工作。 在移动设备上,它们必须像其他应用一样可以安装。 PWA 中的所有网络流量都应该加密。

使用 create-react-app 创建的应用在默认情况下是渐进的。 如果应用使用来自服务器的数据,则使其逐步进行需要工作。 离线功能通常是在service workers的帮助下实现的。

Microservice architecture

【微服务架构】

在本课程中,我们仅仅触及了服务器端的皮毛。 在我们的应用中,我们有一个单体monolithic 后端,这意味着一个应用组成一个整体并在单个服务器上运行,只服务于几个 api 端点。

随着应用的增长,整体后端方法开始在性能和可维护性方面出现问题。

微服务体系结构(microservices)是一种将应用的后端与许多独立的服务组合在一起的方法,这些服务通过网络相互通信。 单独的微服务的目的是照顾一个特定的逻辑功能整体。 在纯微服务体系结构中,服务不使用共享数据库。

例如,bloglist 应用可以由两个服务组成: 一个处理用户,另一个处理 blog。 用户服务的职责是用户注册和用户身份验证,而博客服务将负责与博客相关的操作。

下面的图片显示了基于微服务架构的应用和基于更传统单体结构的应用的结构差异:

fullstack content

前面的角色(图片中被一个正方形包围)在两个模型之间没有太大的不同。 在微服务和前端之间通常有一个所谓的API 网关 ,它提供了一种更加传统的“同一服务器上的所有东西”的幻觉

微服务体系结构的出现和发展是为了满足大规模互联网应用的需要。 这种趋势早在微服务这个词出现之前就由亚马逊设定了。 关键的起点是亚马逊 CEO 杰夫 · 贝索斯在2002年发给所有员工的一封电子邮件:

All teams will henceforth expose their data and functionality through service interfaces. 今后,所有团队都将通过服务接口公开其数据和功能。

Teams must communicate with each other through these interfaces. 团队必须通过这些接口彼此沟通。

There will be no other form of inter-process communication allowed: no direct linking, no direct reads of another team’s data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network. 不允许使用其他形式的行程间通讯: 不允许直接链接,不允许直接读取其他团队的数据存储,不允许共享内存模型,不允许任何后门。 只允许通过网络上的服务接口调用进行通信。

你使用什么技术并不重要。

All service interfaces, without exception, must be designed from the ground up to be externalize-able. That is to say, the team must plan and design to be able to expose the interface to developers in the outside world. 所有的服务接口,无一例外,必须从头开始设计,使其具有可外部化的特性。 也就是说,团队必须进行规划和设计,以便能够将界面暴露给外部世界的开发人员。

No exceptions. 没有例外。

Anyone who doesn’t do this will be fired. Thank you; have a nice day! 不这样做的人将被解雇。谢谢,祝你今天愉快!

如今,微服务使用的最大先驱之一是 Netflix

微型服务的使用已经被大肆宣传成为当今的一种银弹silver bullet,它被用来解决几乎所有的问题。 然而,在应用微服务体系结构时会遇到很多挑战,而且通过最初创建一个传统的包含所有内容的后端,首先使用单体优先monolith first可能是有意义的。 或者也许不是。 关于这个问题有很多不同的意见。 这两个链接都指向马丁 · 福勒的网站; 正如我们所看到的,即使是聪明人也不能完全确定哪一种正确的方式更正确。

不幸的是,我们不能在本课程中更深入地探讨这个重要的议题。 即使只是粗略地看一下这个问题,也需要至少5个星期的时间。

Serverless

在2014年底 Amazon 发布了lambda之后,web 应用开发中出现了一个新的趋势: 无服务器

Lambda 的主要特点是,它支持在云中执行单个函数,如今 Google 的Cloud函数以及Azure相似的函数也是如此。 以前,云中最小的可执行单元是一个进程,例如一个运行 Node 后端的执行期函式库。

例如,使用 Amazon 的API 网关 ,可以制作无服务器的应用,其中对定义的 HTTP API 的请求可以直接从云函数中获得响应。 通常,这些函数已经使用云服务数据库中存储的数据进行操作。

无服务器并不是说应用中没有服务器,而是说服务器是如何定义的。 软件开发人员可以将他们的编程工作转移到更高的抽象级别,因为不再需要通过编程方式定义 http 请求的路由、数据库关系等,因为云基础设施提供了所有这些。 云函数也有助于创建良好的扩展系统,例如亚马逊的 Lambda 每秒可以执行大量的云函数。 所有这些都是通过基础设施自动完成的,不需要启动新的服务器等等。

Useful libraries and interesting links

【有用的库和有趣的链接】

开发者社区已经产生了大量有用的库。 如果你正在开发更实质性的东西,那么检查一下现有的解决方案是否已经可用是值得的。

找到库的一个好地方是 https://applibslist.xyz/

下面列出了一些可信任方推荐的库。

如果您的应用必须处理复杂的数据lodash ,这是我们在第4章中推荐使用的一个很好的库。 如果您更喜欢函数式编程风格,您可以考虑使用ramda

如果你正在处理时间和日期,moment和一个新版本date-fns提供了很好的工具。

Formikredux-form可以用来更容易地处理表单。

如果你的应用显示图表,你可以从多个选项中进行选择,推荐使用rechartshighcharts

由 Facebook 维护的immutable.js-library,顾名思义,提供了一些数据结构的不可变实现。 当我们使用 Redux 时,这个库可能是有用的,因为我们记得 来自第6章节: reducers 必须是纯函数,这意味着它们不能修改存储的状态,而是必须在发生变化时用一个新的状态替换它。 在过去的一年里,一些不可变的 js 的流行已经被Immer 接管了,它提供了类似的功能,但是在一个相对简单的包中。

Redux-saga提供了另一种方法,用于为redux thunk制作异步操作,类似于第6章节。 有些人欣然接受这种炒作,并且喜欢这种炒作。 我不这么认为。

对于单页应用来说,收集用户和页面交互的分析数据比传统的加载整个页面的网页应用 更具有挑战性React Google Analytics 数据库提供了一个解决方案。

在使用 Facebook 非常流行的 React Native 库开发移动应用时,你可以利用你的 React 知道如何开发。

当涉及到用于管理和捆绑 JavaScript 项目的工具时,社区变化很大。 最佳实践发生了迅速的变化(年份是近似值,没有人记得那么久以前) :

在 webpack 开始主导市场之后,赶时髦的人似乎对工具开发失去了兴趣。 几年前,Parcel开始以简单(Webpack 绝对不是)和快于 Webpack 的方式推销自己。 然而,在一个有希望的开始后,Parcel 并没有聚集任何动力,而且它开始看起来将不会是 Webpack 的终结者。

网站 https://reactpatterns.com/ 提供了一个简明的React最佳实践列表,其中一些已经在本课程中熟悉了。 另一个类似的列表是react bits

Reactiflux 一个很大的React开发者不和谐的聊天社区。 在课程结束后,它可能是一个可能的获得支持的地方。 例如,许多库都有自己的频道。

如果您知道一些可推荐的链接或库,请提出PR!