react中使用构建缓存
by Matthew Choi
由Matthew Choi
(Building Tesla’s Battery Range Calculator with React (Part 1))
In this series of articles, I will walk you through the process of building Tesla’s battery range calculator with React.
在本系列文章中,我将引导您完成使用React构建Tesla电池范围计算器的过程。
In this tutorial, we’ll build the React version of Todd Motto’s Building Tesla’s battery range calculator with Angular 2 reactive forms.
在本教程中,我们将使用Angular 2React形式构建 Todd Motto的Building Tesla电池范围计算器的React版本。
So this post will reuse some materials (data, images, and css style). We will focus on rebuilding it in React way.
因此,本文将重用一些资料(数据,图像和CSS样式)。 我们将专注于以React方式对其进行重建。
This is the final GIF image of our application.
这是我们应用程序的最终GIF图像。
? Check out the live version before we get started.
? 在开始之前,请先检查一下最新版本 。
? You can also check out the source code.
? 您也可以签出我们的代码。
Now let’s create the application step by step.
现在,让我们逐步创建应用程序。
Note that you may need some basic React knowledge to follow this tutorial. See the following resources:
请注意,您可能需要一些基本的React知识才能遵循本教程。 请参阅以下资源:
- React Official Documentation React官方文档
- React: Getting Started and Concepts React:入门和概念
(1. Project Setup and create-react-app)
(1.1 Requirements)
The tools and versions I used during the implementation of this app:
我在实现此应用程序时使用的工具和版本:
node v7.3.0npm v3.10.10
(1.2 create-react-app)
creat-react-app is a new tool open-sourced by Facebook for fast react application development, which allows you to easily start React applications without complex setups. You can easily install our project react-tesla-range-calculator
and start the application right away with the following command:
creat-react-app是Facebook开源的用于快速React应用程序开发的新工具,它使您无需复杂的设置即可轻松启动React应用程序。 您可以轻松安装我们的项目react-tesla-range-calculator
并使用以下命令立即启动应用程序:
- npm install -g create-react-app
- create-react-app react-tesla-range-calculator
- cd react-tesla-range-calculator
- npm start
Create a new application through creat-react-app
and open http://localhost:3000/
to check the generated application.
通过creat-react-app
创建一个新应用creat-react-app
然后打开http://localhost:3000/
来检查生成的应用程序。
If you see the screen below, the project has been successfully set up.
如果您看到下面的屏幕,则说明项目已成功设置。
Before we start the project, we need to touch the project source structure. Just leave the files we need for the project and delete the rest. (Delete App.test.js, logo.svg)
在开始项目之前,我们需要触摸项目源代码结构。 只需保留项目所需的文件,然后删除其余文件即可。 (删除App.test.js,logo.svg)
Now our src directory should look like this:
现在,我们的src目录应如下所示:
src - App.css - App.js - index.css - index.js
Here is project source structure :
这是项目源代码结构:
(1.3 Project Entry Point)
First we need to set the entry point to start our Tesla app. Thankfully it’s already created by create-react-app
.
首先,我们需要设置入口点以启动我们的Tesla应用程序。 幸运的是,它已经由create-react-app
。
src/App.js
is the entry point for our app.
src/App.js
是我们应用程序的切入点。
First up, change your App.js
to this:
首先,将您的App.js
更改为:
import React, { Component } from 'react';import './App.css';
class App extends Component { render() { return ( <div> <h2>Let's get started</h2> </div> ); }}
export default App;
When you save the file, it will be automatically compiled and you can see the updated screen.
保存文件时,将自动编译该文件,您可以看到更新的屏幕。
(1.4 Project images/assets)
All images required for this project can be downloaded from:
该项目所需的所有图像都可以从以下网站下载:
- images Download 图片下载
- favicon.ico Download favicon.ico 下载
Unpack assets.zip
and place all images in the src/assets
directory and place the downloaded favicon.ico
in the source root.
解压缩assets.zip
并将所有图像放在src/assets
目录中,并将下载的favicon.ico
放在源根目录中。
react-tesla-range-calculator/src/assets
Any time you feel like if you’ve missed something or unsure if you’re doing right, you can refer to the source code as a reference.
任何时候,如果您想错过某件事或不确定自己做得是否正确,都可以参考源代码作为参考。
(1.5 Data service)
The data you can get from Tesla site is hard-coded and very large, so we’ll use Todd’s new version of the data to make it easier to use. link
您可以从Tesla站点获得的数据是硬编码的,并且非常大,因此我们将使用Todd的新版本数据来简化使用。 链接
We do not use the Injectable decorator
used in Angular2, so we will copy only the export
part, just save it in src/services/BatteryService.js
for now. Later, we will use import
it in TeslaBattery
.
我们不使用Angular2中使用的Injectable decorator
,因此我们仅复制export
部分,仅将其保存在src/services/BatteryService.js
中。 稍后,我们将在TeslaBattery
容器中使用import
。
We will revisit this data service later.
我们稍后将重新访问该数据服务。
(2. Breaking Down the UI)
Almost all React application UIs consist of a composition of components. For example, a weather app consists of a component that displays a local name, a component that displays the current temperature, and a graph component that represents a five-day forecast. For this reason, it is a good idea to decompose the UI into component units before developing the React app.
几乎所有的React应用程序UI都由组件组成。 例如,天气应用程序由显示本地名称的组件,显示当前温度的组件和表示五天天气预报的图形组件组成。 因此,在开发React应用程序之前,最好将UI分解为组件单元。
See Thinking in React for an approach to looking at an application as a combination of components.
有关将应用程序视为组件组合的方法,请参见React中的思考 。
The layout of this application is shown below
该应用程序的布局如下所示
The UI is represented by a component tree as follows.
UI由组件树表示,如下所示。
<App> -- Application entry point <Header></Header> <TeslaBattery> -- Container <TeslaCar /> -- Presentational Component <TeslaStats /> -- Presentational Component <TeslaCounter /> -- Presentational Component <TeslaClimate /> -- Presentational Component <TeslaWheels /> -- Presentational Component <TeslaNotice /> -- Presentational Component </TeslaBattery></App>
(2.1 Container and Presentational Components)
In the above mentioned component tree, we can see that it is classified as Container Component
and Presentational Component
.
在上面提到的组件树中,我们可以看到它被分类为Container Component
和Presentational Component
。
This is a useful pattern that can be used when developing an application with React. It is easier to reuse by dividing components into two categories.
这是一个有用的模式,可以在使用React开发应用程序时使用。 通过将组件分为两类,可以更轻松地重用。
* Container Component (stateful component): - Are concerned with how things work. - In general, except for some wrapping divs, they do not have their own DOM markup and have no style. - Provide data and actions to presentational or other components. - Are often stateful, as they tend to serve as data sources.
* Presentational Component (stateless component): - Are concerned with how things look. - Usually have some DOM markup and styles of their own. - Receive data and callbacks exclusively via props. - Rarely have their own state (when they do, it’s UI state rather than data).
What are the benefits of using these patterns?
使用这些模式有什么好处?
- Better separation of concerns
- Better reusability
- Extract layout components to prevent duplication
For more details, see Presentational and Container Components
有关更多详细信息,请参见外观和容器组件。
(3. Header component)
Let’s create our first React component, Header
. The Header
component is simply a black bar with the Tesla logo and text.
让我们创建第一个React组件Header
。 Header
组件只是带有特斯拉徽标和文字的黑条。
Create the src/components/Header
directory, create a Header.js
file in it, and enter the following code:
创建src/components/Header
目录,在其中创建Header.js
文件,然后输入以下代码:
import React from 'react';import './Header.css';import logoUrl from '../../assets/logo.svg';
const Header = () => ( <div className="header"> <img src={logoUrl} alt="Tesla" /> </div>)
export default Header;
Here, the component is in the form of a function (
ES6 Arrow Function
). A component declared in this form is called afunctional component
. If there is nostate
and thelifecycle
method is not needed, it is a good pattern to declare it as a function type. Functional components are suitable forPresentational Component
because they have no state and they depend only on theprops
that is received from higher components.在此,组件采用函数形式(
ES6 Arrow Function
)。 以这种形式声明的组件称为functional component
。 如果没有state
并且不需要lifecycle
方法,则将其声明为函数类型是一个很好的模式。 功能组件适用于Presentational Component
因为它们没有状态,并且仅取决于从更高组件接收到的props
。
(3.1 Header Component Style)
Create a Header.css
file in the src/components/Header
directory and type the following style:
在src/components/Header
目录中创建一个Header.css
文件,然后键入以下样式:
.header { padding: 25px 0; text-align: center; background: #222;}
.header img { width: 100px; height: 13px;}
There are a number of ways to apply styles to components, but here we will create each component directory in the
src/components
directory and pairjs
andcss
files each time we create a component.有多种将样式应用于组件的方法,但是在这里,我们将在
src/components
目录中创建每个组件目录,并在每次创建组件时将js
和css
文件配对。
(3.2 Import Header component in App Container)
Now that you’ve created the Header
component, let’s use import
in the entry point App.js
.
现在,您已经创建了Header
组件,让我们在入口点App.js
使用import
。
import React, { Component } from 'react';import './App.css';import Header from './components/Header/Header';
class App extends Component { render() { return ( <div className="App"> <Header /> </div> ); }}
export default App;
When you save all the modified files, they will be updated automatically and you should see the Tesla logo as follows:
保存所有修改后的文件后,它们将自动更新,您应该看到Tesla徽标,如下所示:
(4. TeslaBattery Container)
In our app, the TeslaBattery
component is responsible for creating and managing data and state as Container Component
, passing it to other Presentational Components
, performing a callback function and changing its state.
在我们的应用程序中, TeslaBattery
组件负责创建和管理数据和状态(作为Container Component
,将其传递给其他Presentational Components
,执行回调函数并更改其状态。
By inheriting React.Component
, TeslaBattery
must have a render
method, optionally it can initialize its state through the constructor
, and implement other methods such as lifecycle callbacks.
通过继承React.Component
, TeslaBattery
必须具有render
方法,可以选择通过constructor
初始化其状态,并实现其他方法,例如生命周期回调。
lifecycle callbacks
are useful when you want to render or update components, or to receive notifications at different stages of lifecycle
.
当您要呈现或更新组件,或在lifecycle
不同阶段接收通知时, lifecycle
lifecycle callbacks
非常有用。
Create the src/containers
directory, create a TeslaBattery.js
file in it, and enter the following code:
创建src/containers
目录,在其中创建一个TeslaBattery.js
文件,然后输入以下代码:
import React from 'react';import './TeslaBattery.css';
class TeslaBattery extends React.Component { render() { return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> </form> ) }}
export default TeslaBattery;
(4.1 TeslaBattery Container Style)
TeslaBattery.css
only holds a minimal style.
TeslaBattery.css
仅具有最小风格。
.tesla-battery { width: 1050px; margin: 0 auto;}
.tesla-battery h1 { font-family: 'RobotoNormal'; font-weight: 100; font-size: 38px; text-align: center; letter-spacing: 3px;}
The components to be created in the future will be configured in the TeslaBattery
sequentially.
将来要创建的组件将在TeslaBattery
容器中依次配置。
(5. TeslaNotice Component)
Let’s create a static text part with a TeslaNotice
component.
让我们用TeslaNotice
组件创建一个静态文本部分。
Create the src/components/TeslaNotice
directory, create a TeslaNotice.js
file in it, and enter the following code:
创建src/components/TeslaNotice
目录,在其中创建一个TeslaNotice.js
文件,然后输入以下代码:
import React from 'react';import './TeslaNotice.css';
const TeslaNotice = () => ( <div className="tesla-battery__notice"> <p> The actual amount of range that you experience will vary based on your particular use conditions. See how particular use conditions may affect your range in our simulation model. </p> <p> Vehicle range may vary depending on the vehicle configuration, battery age and condition, driving style and operating, environmental and climate conditions. </p> </div>)
export default TeslaNotice;
(5.1 TeslaNotice Component Style)
Next up, create src/components/TeslaNotice
directory, create TeslaNotice.css
in it and add these styles to your TeslaNotice.css
file:
接下来,创建src/components/TeslaNotice
目录,在其中创建TeslaNotice.css
并将以下样式添加到您的TeslaNotice.css
文件中:
.tesla-battery__notice { margin: 20px 0; font-size: 15px; color: #666; line-height: 20px;}
(5.2 Import TeslaNotice component in TeslaBattery Container)
Next, import TeslaNotice
component in TeslaBattery.js
:
接下来,在TeslaBattery.js
导入TeslaNotice
组件:
...import TeslaNotice from '../components/TeslaNotice/TeslaNotice';
class TeslaBattery extends React.Component { render() { return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> <TeslaNotice /> </form> ) }}...
We will continue in such a way that components are created in this pattern and imported from the
TeslaBattery
.我们将继续以这种方式创建组件并从
TeslaBattery
容器导入组件的方式。
(6. TeslaCar Component)
Now let’s render a nice Tesla car image with wheel animation.
现在,让我们用车轮动画渲染漂亮的特斯拉汽车图像。
Create the src/components/TeslaCar
directory, create a TeslaCar.js
file in it, and inside your TeslaCar.js
file :
创建src/components/TeslaCar
目录,在其中创建一个TeslaCar.js
文件,并在您的TeslaCar.js
文件内部:
import React from 'react';import './TeslaCar.css';
const TeslaCar = (props) => ( <div className="tesla-car"> <div className="tesla-wheels"> <div className={`tesla-wheel tesla-wheel--front tesla-wheel--${props.wheelsize}`}></div> <div className={`tesla-wheel tesla-wheel--rear tesla-wheel--${props.wheelsize}`}></div> </div> </div>);
TeslaCar.propTypes = { wheelsize: React.PropTypes.number}
export default TeslaCar;
Here we specify propTypes
using the React built-in typechecking. In development mode, React checks props
passed to the component. (Only in development mode for performance reasons)
在这里,我们使用React内置的类型propTypes
来指定propTypes
。 在开发模式下,React检查传递给组件的props
。 (仅出于性能考虑处于开发模式)
For each props
attribute, React attempts to find it in the component’s propType
object to determine whether (1) prop is expected (2) prop is the correct type. In this case, the TeslaCar
component expects the props
attribute wheelsize
and specifies that it is a number
type. If the wrong value is provided, a warning appears in the JavaScript console, which is useful for fixing potential bugs in early stage.
对于每个props
属性,React尝试在组件的propType
对象中找到它,以确定是否(1)prop是期望的(2)prop是正确的类型。 在这种情况下, TeslaCar
组件希望props
属性的wheelsize
并指定它是number
类型。 如果提供了错误的值,则会在JavaScript控制台中显示一条警告,这对于在早期修复潜在的错误很有用。
More information on
React.PropTypes
can be found here有关
React.PropTypes
更多信息可以在这里找到Update: New Deprecation Warnings in React 15.5
更新:React 15.5中的新弃用警告
In 15.5, instead of accessing
PropTypes
from the mainReact
object, install theprop-types
package and import them from there:在15.5中,安装
prop-types
包并从那里导入它们,而不是从主React
对象访问PropTypes
:
https://facebook.github.io/react/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes
https://facebook.github.io/react/blog/2017/04/07/react-v15.5.0.html#migrating-from-react.proptypes
// Before (15.4 and below) import React from 'react';
import React from 'react';import './TeslaCar.css';
.........................
TeslaCar.propTypes = { wheelsize: React.PropTypes.number}
export default TeslaCar;
// After (15.5) import React from 'react'; import PropTypes from 'prop-types';import './TeslaCar.css';
...........................
TeslaCar.propTypes = { wheelsize: PropTypes.number} export default TeslaCar;
(6.1 TeslaCar Component Style)
Next, create a TeslaCar.css
file in the src/components/TeslaCar
directory and give it the following style. Since the code is long and omitted here, let’s check the source code.
接下来,在src/components/TeslaCar
目录中创建TeslaCar.css
文件,并为其指定以下样式。 由于代码很长,在这里省略了,让我们检查源代码 。
.tesla-car { width: 100%; min-height: 350px; background: #fff url(../../assets/tesla.jpg) no-repeat top center; background-size: contain; }
.tesla-wheels { height: 247px; width: 555px; position: relative; margin: 0 auto; }
...
This gives us our animations and the component base for the car, which is displayed as background images.
这为我们提供了动画和汽车的零部件库,将其显示为背景图像。
(6.2 Import TeslaCar component in TeslaBattery Container)
Next, we need to add this component to our again. Import TeslaNotice
component in TeslaBattery.js
:
接下来,我们需要再次将此组件添加到我们的容器中。 在TeslaBattery.js
导入TeslaNotice
组件:
...import TeslaCar from '../components/TeslaCar/TeslaCar';
class TeslaBattery extends React.Component { render() { return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> <TeslaCar /> <TeslaNotice /> </form> ) }}...
Here’s what you should be seeing:
这是您应该看到的:
(7. Props and React Developer Tools)
Wow! It’s nice but something is missing. The wheels are not shown. Let’s look for the cause. According to the source code, TeslaCar
should be passed to props
and class name changed based on props.wheelsize
.
哇! 很好,但是缺少某些东西。 车轮未显示。 让我们寻找原因。 根据源代码, TeslaCar
应该传递给props
并且基于props.wheelsize
更改类名。
In other words, you need to receive some data (in this case, wheelsize) from the parent component and render it properly, and there must be a communication method that can receive the data.
换句话说,您需要从父组件接收一些数据(在这种情况下为wheelsize)并正确渲染它,并且必须有一种可以接收数据的通信方法。
React is composed of a component tree, which consists of a for delivering data and state, and a component for passively receiving data and state from a. The tool that delivers this state to the subcomponents is a single object, props
.
React由一个组件树组成,该树由一个用于传递数据和状态的容器,以及一个用于从容器被动接收数据和状态的组件组成。 将此状态传递给子组件的工具是单个对象props
。
You can easily understand this by checking the component tree using React Developer Tools in Chrome.
您可以通过使用Chrome中的React Developer Tools检查组件树来轻松理解这一点。
props
is a JavaScript single object, in this case an empty object. This is because we did not pass props
in the parent component TeslaBattery
.
props
是JavaScript单个对象,在这种情况下为空对象。 这是因为我们没有在父组件TeslaBattery
传递props
。
(8. State of Application)
We need to think about what state
is required to be managed in our app. If you look at the final app GIF image at the top of this article, the state values are:
我们需要考虑在我们的应用程序中需要管理什么state
。 如果您查看本文顶部的最终应用GIF图像,则状态值为:
- carstats (object array) : An array of battery numerical value objects by car model according to the currently selected condition value (speed, temperature, climate, wheel)
carstats(对象数组) :根据当前选择的条件值(速度,温度,气候,车轮)按汽车模型排列的电池数值对象数组 - config (object): Currently selected conditions object (speed: 55, temperature: 20, climate: aricon on, wheel: 19)
配置(对象) :当前选择的条件对象(速度:55,温度:20,气候:aricon开,滚轮:19)
That is the single source of truth for our app. Now we will add the constructor method to the TeslaBattery
and set the initial value so that we can manage this state value and pass it to the subcomponent. The TeslaCar
component accepts the wheelsize
input through props
and renders the Tesla car image and spins the wheels.
这是我们应用程序的唯一事实来源。 现在,我们将构造函数方法添加到TeslaBattery
容器并设置初始值,以便我们可以管理该状态值并将其传递给子组件。 TeslaCar
组件通过props
接受wheelsize
输入,并渲染Tesla汽车图像并旋转车轮。
Both the parent component and the child component do not know whether a particular component is stateful or stateless and do not care whether it is defined as a
function
or aclass
. This is why the state is often called local or encapsulated. This state can not be accessed by components other than the component that owns and sets the state. So this state value can be passed to the sub-component asprops
. This is commonly referred to as a “top-down” or “unidirectional” data flow. Every state is always owned by a particular component, and any data or UI derived from that state only affects the “downward” component of the tree.父组件和子组件都不知道特定组件是有状态的还是无状态的,并且不在乎将其定义为
function
还是class
。 这就是为什么状态通常被称为局部状态或封装状态的原因。 除拥有和设置状态的组件外,其他组件无法访问此状态。 因此,该状态值可以作为props
传递给子组件。 这通常称为“自上而下”或“单向”数据流。 每个状态始终由特定组件拥有,并且从该状态派生的任何数据或UI仅会影响树的“向下”组件。
...class TeslaBattery extends React.Component { constructor(props) { super(props);
this.state = { carstats: [], config: { speed: 55, temperature: 20, climate: true, wheels: 19 } } } render() { // ES6 Object destructuring Syntax, // takes out required values and create references to them const { config } = this.state; return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> <TeslaCar wheelsize={config.wheels}/> <TeslaNotice /> </form> ) }}...
In render()
, the code in the form const {a, b} = c
is ES6 Object Destructuring
. It takes the required value out of the object and makes a reference to it.
在render()
,形式为const {a, b} = c
是ES6 Object Destructuring
。 它从对象中取出所需的值并对其进行引用。
Conceptually, the React component is like a JavaScript function and receives an arbitrary input called ‘props’ and returns a React element that describes what should be shown.
从概念上讲,React组件就像一个JavaScript函数,它接收称为“ props”的任意输入,并返回描述应显示内容的React元素。
In a word, this concept can be expressed by the following formula.
总之,可以通过以下公式来表达该概念。
fn(d) = V
fn(d)= V
A function that receives data as input and returns a view.
接收数据作为输入并返回视图的函数。
If you save files, you can see that the rendered Tesla car and wheel animation work well on the updated screen. You can also see that props
is passed well in the component tree.
如果保存文件,则可以在更新的屏幕上看到渲染的特斯拉汽车和车轮动画效果良好。 您还可以看到props
在组件树中传递得很好。
Some functions are called “pure” in the sense that they always return the same output value if they have the same input value without changing the input value. (
Pure function
) One important React strict rule here is that all React components should behave like pure functions with respect to props.props
must be read-only.在某些函数被称为“纯”的意义上,如果它们具有相同的输入值而不更改输入值,它们总是会返回相同的输出值。 (
Pure function
)一个重要的React严格规则是,所有React组件的行为都应与props的纯函数类似。props
必须是只读的。
(9. TeslaStats Component)
Now we are going to build the TeslaStats
component. Create the src/components/TeslaStats
directory, create a TeslaStats.js
file in it, and enter the following code:
现在,我们将构建TeslaStats
组件。 创建src/components/TeslaStats
目录,在其中创建一个TeslaStats.js
文件,然后输入以下代码:
import React from 'react';import './TeslaStats.css';
const TeslaStats = (props) => { const listItems = props.carstats.map((stat) => ( <li key={stat.model}> <div className={`tesla-stats-icon tesla-stats-icon--${stat.model.toLowerCase()}`}></div> <p>{stat.miles}</p> </li> )); return ( <div className="tesla-stats"> <ul> {listItems} </ul> </div> )};
TeslaStats.propTypes = { carstats: React.PropTypes.array}
export default TeslaStats;
TeslaStats
is also a presentational component
that receives state, and it takes a list of arrays containing model values by props
and renders them.
TeslaStats
也是一个接收状态的presentational component
,它通过props
包含模型值的数组列表并进行渲染。
First, let’s consider how to transform a list in JavaScript. The following code uses the map()
function to take a numbers
array and return a double value.
首先,让我们考虑如何在JavaScript中转换列表。 以下代码使用map()
函数获取一个numbers
数组并返回一个双精度值。
This code prints [2, 4, 6, 8, 10]
to the console.
此代码将[2, 4, 6, 8, 10]
2、4、6、8、10]打印到控制台。
const numbers = [1, 2, 3, 4, 5];const doubled = numbers.map((number) => number * 2);console.log(doubled);
Converting an array to a list in React is almost identical. Here we use the JavaScript map
function to iterate through the props.carstats
array.
在React中将数组转换为列表几乎相同。 在这里,我们使用JavaScript map
函数来遍历props.carstats
数组。
For each iteration, it returns a <
li> element containing the
model and
a <li>element surroun
ding the <p> ta
g containing miles.
对于每次迭代,它返回一个<
LI>元件containin g the
模型and
一个<LI>元件SURR oun
丁的<宝洁t; ta
t; ta
包含英里。
Finally, it returns the listItems
array in the <
ul> element.
最后,它在<
ul>元素中返回listItems
数组。
(9.1 TeslaStats Component Style)
Next, create a TeslaStats.css
file in the src/components/TeslaStats
directory and type the following style. Since the code is long and omitted here, let’s check the source code
接下来,在src/components/TeslaStats
目录中创建TeslaStats.css
文件,然后键入以下样式。 由于代码很长,这里省略了,让我们检查一下源代码
....tesla-stats { margin: -70px 0 30px; }.tesla-stats ul { text-align: center; }...
The task that this component performs is to iterate through the props.carstats
array and bind a particular class to an element based on stat.model
. You can then replace the background image to display the Tesla model.
该组件执行的任务是遍历props.carstats
数组,并将特定类绑定到基于stat.model
的元素。 然后,您可以替换背景图像以显示Tesla模型。
(9.2 Import TeslaStats component in TeslaBattery Container)
Then add following import
to use the TeslaStats
component in TeslaBattery.js
.
然后添加以下import
使用TeslaStats
成分TeslaBattery.js
。
...import TeslaStats from '../components/TeslaStats/TeslaStats';...render() { const { config, carstats } = this.state; return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> <TeslaCar wheelsize={config.wheels}/> <TeslaStats carstats={carstats}/> <TeslaNotice /> </form> )}...
We need to pass the carstats
array to props
, so let’s set the value using BatteryService
we’ve already implemented.
我们需要将carstats
数组传递给props
,所以让我们使用已经实现的BatteryService
设置值。
(9.3 CalculateStats and setState)
Add import getModelData
first.
首先添加导入getModelData
。
After the component is mounted via componentDidMount()
, it calls the statsUpdate()
function. When calculateStats()
function that receives carModels
and the current state value as the input is executed, the object with the matching model
and miles
values is returned, and the return value is passed through the setState()
and then state object is updated.
通过componentDidMount()
挂载componentDidMount()
,它将调用statsUpdate()
函数。 当执行接收carModels
和当前状态值作为输入的calculateStats()
函数时,将返回具有匹配model
和miles
值的对象,并将返回值通过setState()
传递,然后更新状态对象。
...import { getModelData } from '../services/BatteryService';...
calculateStats = (models, value) => { const dataModels = getModelData(); return models.map(model => { // ES6 Object destructuring Syntax, // takes out required values and create references to them const { speed, temperature, climate, wheels } = value; const miles = dataModels[model][wheels][climate ? 'on' : 'off'].speed[speed][temperature]; return { model, miles }; });} statsUpdate() { const carModels = ['60', '60D', '75', '75D', '90D', 'P100D']; // Fetch model info from BatteryService and calculate then update state this.setState({ carstats: this.calculateStats(carModels, this.state.config) }) } componentDidMount() { this.statsUpdate(); }...
One caveat is that explicit binding in the TeslaBattery
constructor function is required to access this
in the class.
一个需要注意的是,明确结合在TeslaBattery
构造函数才能访问this
班上。
...this.calculateStats = this.calculateStats.bind(this);this.statsUpdate = this.statsUpdate.bind(this);...
(9.4 Add Additional Style)
Additional styling is required for a nice layout here.
这里的布局不错,还需要其他样式。
First open the src/index.css
file and delete all existing code and add the following:
首先打开src/index.css
文件并删除所有现有代码并添加以下内容:
@font-face { font-family: 'RobotoNormal'; src: url('./assets/fonts/Roboto-Regular-webfont.eot'); src: url('./assets/fonts/Roboto-Regular-webfont.eot?#iefix') format('embedded-opentype'), url('./assets/fonts/Roboto-Regular-webfont.woff') format('woff'), url('./assets/fonts/Roboto-Regular-webfont.ttf') format('truetype'), url('./assets/fonts/Roboto-Regular-webfont.svg#RobotoRegular') format('svg'); font-weight: normal; font-style: normal;}
*, *:before, *:after { box-sizing: border-box; margin: 0; padding: 0; font: 300 14px/1.4 'Helvetica Neue', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased;}
.cf:before,.cf:after { content: ''; display: table;}.cf:after { clear: both;}.cf { *zoom: 1;}
Next, open the src/App.css
file and delete all existing code and add the following:
接下来,打开src/App.css
文件并删除所有现有代码并添加以下内容:
.wrapper { margin: 100px 0 150px;}
The work result screen so far is as follows.
到目前为止的工作结果画面如下。
(10. Reusable TeslaCounter Component)
Tesla’s speed and external temperature controls should be reusable components, so I’ll make them a generic Counter component that allows for other metadata such as step, minimum, maximum, and title and units (mph / degrees).
特斯拉的速度和外部温度控制应该是可重用的组件,因此我将使它们成为通用的Counter组件,该组件允许其他元数据,例如步长,最小,最大以及标题和单位(英里/度)。
Also, unlike the components we have created so far, we need an action to change the state value in response to user input (button click, checkbox selection, etc.). Let’s look at how to handle events that occur in a subcomponent.
此外,与我们到目前为止创建的组件不同,我们需要一个操作来响应用户输入(按钮单击,复选框选择等)来更改状态值。 让我们看一下如何处理子组件中发生的事件。
Create the src/components/TeslaCounter
directory as before, create a TeslaCounter.js
file in it, and enter the following code:
像以前一样创建src/components/TeslaCounter
目录,在其中创建一个TeslaCounter.js
文件,然后输入以下代码:
import React from 'react';import './TeslaCounter.css';
const TeslaCounter = (props) => ( <div className="tesla-counter"> <p className="tesla-counter__title">{props.initValues.title}</p> <div className="tesla-counter__container cf"> <div className="tesla-counter__item"> <p className="tesla-counter__number"> { props.currentValue } <span>{ props.initValues.unit }</span> </p> <div className="tesla-counter__controls"> <button onClick={(e) => props.increment(e, props.initValues.title)} disabled={props.currentValue >= props.initValues.max} > </button> <button onClick={(e) => props.decrement(e, props.initValues.title)} disabled={props.currentValue <= props.initValues.min} > </button> </div> </div> </div> </div> );
TeslaCounter.propTypes = { currentValue: React.PropTypes.number, increment: React.PropTypes.func, decrement: React.PropTypes.func, initValues: React.PropTypes.object}
export default TeslaCounter;
Let’s think about what we want here. Each time you click and change the speed and temperature, you must update the state so that the value is reflected between the maximum and minimum values.
让我们考虑一下我们想要什么。 每次单击并更改速度和温度时,都必须更新状态,以使该值反映在最大值和最小值之间。
Since the component only needs to update its own state, TeslaBattery
passes the callback (increment
, decrement
) to the TeslaCounter
each time it needs to update its state. You can use the onClick
event on a button to notify the event. The callback passed by TeslaBattery
calls setState()
and the app is updated.
由于该组件仅需要更新其自身的状态, TeslaBattery
, TeslaBattery
需要更新其状态时, TeslaBattery
将回调( increment
, decrement
)传递给TeslaCounter
。 您可以在按钮上使用onClick
事件来通知该事件。 TeslaBattery
传递的回调调用setState()
,并且应用程序已更新。
We will implement a callback that will be passed by TeslaBattery
in a few moments.
我们将实现一个回调,该回调将在几分钟后由TeslaBattery
通过。
(10.1 TeslaCounter Component Style)
Let’s implement the style first. Create a TeslaCounter.css
file in the src/components/TeslaCounter
directory and specify the following styles. Since the code is long and omitted here, let’s check the source code
让我们首先实现样式。 在src/components/TeslaCounter
目录中创建一个TeslaCounter.css
文件,并指定以下样式。 由于代码很长,这里省略了,让我们检查源代码
.tesla-counter { float: left; width: 230px; }.tesla-counter__title { letter-spacing: 2px; font-size: 16px; }...
(10.2 Import TeslaCounter Component in TeslaBattery Container)
Now, we will implement callback
in TeslaBattery
and pass it to the TeslaCounter
component.
现在,我们将在TeslaBattery
实现callback
并将其传递给TeslaCounter
组件。
First, add import
to use the TeslaCounter
component in TeslaBattery.js
.
首先,添加import
使用TeslaCounter
成分TeslaBattery.js
。
We also implement the callback functions increment()
and decrement()
, and the internal function updateCounterState()
and bind it in the constructor
. Then pass the callback
function to the TeslaCounter
component with props
.
我们还实现了回调函数increment()
和decrement()
,以及内部函数updateCounterState()
并将其绑定到constructor
。 然后使用props
将callback
函数传递给TeslaCounter
组件。
...constructor(props) { super(props);
this.calculateStats = this.calculateStats.bind(this); this.statsUpdate = this.statsUpdate.bind(this); this.increment = this.increment.bind(this); this.decrement = this.decrement.bind(this); this.updateCounterState = this.updateCounterState.bind(this);
this.state = { carstats: [], config: { speed: 55, temperature: 20, climate: true, wheels: 19 } } }...updateCounterState(title, newValue) { const config = { ...this.state.config }; // update config state with new value title === 'Speed' ? config['speed'] = newValue : config['temperature'] = newValue; // update our state this.setState({ config }); }
increment(e, title) { e.preventDefault(); let currentValue, maxValue, step; const { speed, temperature } = this.props.counterDefaultVal; if (title === 'Speed') { currentValue = this.state.config.speed; maxValue = speed.max; step = speed.step; } else { currentValue = this.state.config.temperature; maxValue = temperature.max; step = temperature.step; }
if (currentValue < maxValue) { const newValue = currentValue + step; this.updateCounterState(title, newValue); } }
decrement(e, title) { e.preventDefault(); let currentValue, minValue, step; const { speed, temperature } = this.props.counterDefaultVal; if (title === 'Speed') { currentValue = this.state.config.speed; minValue = speed.min; step = speed.step; } else { currentValue = this.state.config.temperature; minValue = temperature.min; step = temperature.step; }
if (currentValue > minValue) { const newValue = currentValue - step; this.updateCounterState(title, newValue); } } ...render() { return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> <TeslaCar wheelsize={config.wheels} /> <TeslaStats carstats={carstats} /> <div className="tesla-controls cf"> <TeslaCounter currentValue={this.state.config.speed} initValues={this.props.counterDefaultVal.speed} increment={this.increment} decrement={this.decrement} /> <div className="tesla-climate-container cf"> <TeslaCounter currentValue={this.state.config.temperature} initValues={this.props.counterDefaultVal.temperature} increment={this.increment} decrement={this.decrement} /> </div> </div> <TeslaNotice /> </form> )}
(10.3 TeslaBattery Container Style)
An additional style is required for TeslaBattery
as soon as the TeslaCounter
component is added. Open the TeslaBattery.css
file and add the following:
添加TeslaCounter
组件后, TeslaBattery
需要其他样式。 打开TeslaBattery.css
文件并添加以下内容:
.tesla-climate-container { float: left; width: 420px; padding: 0 40px; margin: 0 40px 0 0; border-left: 1px solid #ccc; border-right: 1px solid #ccc;}.tesla-controls { display: block; width: 100%;}
(10.4 Default Value Props)
Here, initValues
passed to TeslaCounter
is a constant value and is passed from App
which is a parent component of TeslaBattery
.
在这里,传递给TeslaCounter
initValues
是一个常量值,并从作为TeslaBattery
父组件的App
传递。
Open App.js
and pass the counterDefaultVal
object to the TeslaBattery
component as follows:
开放App.js
和传递counterDefaultVal
对象到TeslaBattery
组分如下:
import React, { Component } from 'react';import './App.css';import Header from './components/Header/Header';import TeslaBattery from './containers/TeslaBattery';
const counterDefaultVal = { speed: { title: "Speed", unit: "mph", step: 5, min: 45, max: 70 }, temperature: { title: "Outside Temperature", unit: "°", step: 10, min: -10, max: 40 }};
class App extends Component { render() { return ( <div className="App"> <Header /> <TeslaBattery counterDefaultVal={counterDefaultVal}/> </div> ); }}
export default App;
Now, when you click Speed and Temperature, you can see that the changed values are updated and re-rendered in the state object through the React Developer Tool
.
现在,当您单击``速度和温度''时,您可以看到通过React Developer Tool
在状态对象中更新并重新渲染了更改的值。
(10.5 Virtual DOM)
What a single-page application can give us is a seamless user experience and smooth interaction.
单页应用程序可以为我们提供无缝的用户体验和流畅的交互。
In our app, car model values are updated without having to reload the entire page every time the user changes speed or temperature. Even if you need to connect to the server to get the data. To provide this user experience, you need to know which part of the DOM
you need to update when changes or interactions occur.
在我们的应用程序中,汽车模型值得以更新,而无需每次用户更改速度或温度时都重新加载整个页面。 即使您需要连接到服务器来获取数据。 为了提供这种用户体验,您需要知道在发生更改或交互时需要更新DOM
哪一部分。
Each JavaScript framework uses a different strategy: Ember
uses data-binding
, Angular1
uses dirty checking, and React
uses Virtual DOM.
每个JavaScript框架使用不同的策略: Ember
使用data-binding
, Angular1
使用脏检查 , React
使用虚拟DOM 。
In React, the first time the component’s rendering method is called, it prints a virtual DOM
model, rather than the actual DOM
element itself. The virtual DOM
is a JavaScript data structure that represents the appearance of DOM
. React then takes this model and creates the actual DOM
element.
在React中,第一次调用组件的呈现方法时,它将打印virtual DOM
模型,而不是实际的DOM
元素本身。 virtual DOM
是表示DOM
外观JavaScript数据结构。 然后,React采用此模型并创建实际的DOM
元素。
Then, whenever the component’s state changes (eg, setState
is called), the rendering method of the component is called and a new virtual DOM
is created, and this new virtual DOM
is compared with the previous virtual DOM
. The result of this comparison is to show the actual DOM
changes and the DOM
will be ‘patched’ with the changes and the screen will change.
然后,只要组件的状态发生变化(例如,调用setState
),就会调用组件的呈现方法并创建一个新的virtual DOM
,并将此新的virtual DOM
与先前的virtual DOM
。 比较的结果是显示实际的DOM
更改,并且DOM
将随更改进行“修补”,并且屏幕也会更改。
The car model information does not change yet as the speed and temperature change. This will eventually be implemented later.
汽车型号信息不会随速度和温度的变化而变化。 最终将在以后实现。
(11. Aircon and Heating Controls)
We monitor the temperature and change the heating
to aircon
when it is more than 20 degrees, and heating
when it is below 20 degrees.
我们监测温度和改变heating
到aircon
当其大于20度,并heating
时,它是20度以下。
First create a directory src/components/TeslaClimate
, create a TeslaClimate.js
file in it, and enter the following code:
首先创建目录src/components/TeslaClimate
,在其中创建一个TeslaClimate.js
文件,然后输入以下代码:
import React from 'react';import './TeslaClimate.css';
const TeslaClimate = (props) => ( <div className="tesla-climate"> <label className={`tesla-climate__item ${props.value ? 'tesla-climate__item--active' : '' } ${!props.limit ? 'tesla-heat':''}`} > <p>{props.limit ? 'ac' : 'heat'} {props.value ? 'on' : 'off'}</p> <i className="tesla-climate__icon"></i> <input type="checkbox" name="climate" checked={props.value} onChange={() => {props.handleChangeClimate()}} /> </label> </div>);
TeslaClimate.propTypes = { value: React.PropTypes.bool, limit: React.PropTypes.bool, handleChangeClimate: React.PropTypes.func}
export default TeslaClimate;
This component changes the style class according to the props.value
passed in, and changes the text according to props.limit
.
此组件根据传入的props.value
更改样式类,并根据props.limit
更改文本。
TeslaBattery
passes callback(handleChangeClimate
in this case) to TeslaClimate
, which is executed whenever the state needs to be updated. onChange
event can be used to notify the event. The callback
passed by TeslaBattery
is called with setState()
to update its state and re-render.
TeslaBattery
通过回调( handleChangeClimate
在这种情况下),以TeslaClimate
,每当要更新的状态需要被执行。 onChange
事件可用于通知该事件。 通过setState()
调用TeslaBattery
传递的callback
以更新其状态并重新呈现。
(11.1 TeslaClimate Component Style)
Create a TeslaClimate.css
file in the src/components/TeslaClimate
directory and specify the following styles. Since the code is long and omitted here, let’s check the source code.
在src/components/TeslaClimate
目录中创建TeslaClimate.css
文件,并指定以下样式。 由于代码很长,在这里省略了,让我们检查源代码 。
.tesla-climate { float: left; } .tesla-climate__item { cursor: pointer; display: block; width: 100px; height: 100px; border: 6px solid #f7f7f7; border-radius: 50%; box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3); color: #666; background: #fff; } ...
(11.2 Import TeslaClimate Component in TeslaBattery Container)
Now we will implement callback
in TeslaBattery
and pass it to the TeslaClimate
component.
现在,我们将在TeslaBattery
实现callback
并将其传递给TeslaClimate
组件。
First, add import
to use the TeslaClimate
component in TeslaBattery.js
. We implement callback function handleChangeClimate()
and bind it in constructor()
. Then pass the callback function to the TeslaClimate
component as props
.
首先,添加import
使用TeslaClimate
成分TeslaBattery.js
。 我们实现回调函数handleChangeClimate()
并将其绑定到constructor()
。 然后将回调函数作为props
传递给TeslaClimate
组件。
...import TeslaClimate from '../components/TeslaClimate/TeslaClimate';...constructor(props) { super(props); ... this.handleChangeClimate = this.handleChangeClimate.bind(this); ...}// handle aircon & heating click event handlerhandleChangeClimate() { const config = {...this.state.config}; config['climate'] = !this.state.config.climate; this.setState({ config });}
...<TeslaClimate value={this.state.config.climate} limit={this.state.config.temperature > 10} handleChangeClimate={this.handleChangeClimate}/> ...
Now the state value changes according to the temperature change, and when the changed value is passed to the TeslaClimate
component, the style class and text are changed according to the value.
现在状态值根据温度变化而变化,并且将更改后的值传递给TeslaClimate
组件时,样式类和文本也会根据该值而变化。
(12. TeslaWheels Component)
Finally, let’s make the final component TeslaWheels
. As always, create a directory src/components/TeslaWheels
, create a TeslaWheels
file in it, and enter the following code.
最后,让我们制作最终组件TeslaWheels
。 与往常一样,创建目录src/components/TeslaWheels
,在其中创建一个TeslaWheels
文件,然后输入以下代码。
import React from 'react';import './TeslaWheels.css';
const LabelLists = (props) => { const value = props.wheels.value; const changeHandler = props.wheels.handleChangeWheels; const sizes = [19, 21]; const LabelItems = sizes.map(size => ( <label key={size} className={`tesla-wheels__item tesla-wheels__item--${size} ${value === size ? 'tesla-wheels__item--active' : '' }`}> <input type="radio" name="wheelsize" value={size} checked={value === size} onChange={() => {changeHandler(size)}} /> <p> {size}" </p> </label> ) ); return ( <div> {LabelItems} </div> );}const TeslaWheels = (props) => ( <div className="tesla-wheels__component"> <p className="tesla-wheels__title">Wheels</p> <div className="tesla-wheels__container cf"> <LabelLists wheels={props}/> </div> </div>);TeslaWheels.propTypes = { value: React.PropTypes.number, handleChangeWheels: React.PropTypes.func}export default TeslaWheels;
Our implementation here is similar to the conversion of the props
array object to a list in the TeslaStats
component. Repeat the props.sizes
array using the javascript map()
function.
我们此处的实现类似于将props
数组对象转换为TeslaStats
组件中的列表。 使用javascript map()
函数重复props.sizes
数组。
For each iteration, it returns the <lab
el> elements containin
g size. Finally, the Labe
lItems list is built into the Tesla
Wheels component and rendered.
对于每次迭代,它返回<lab
EL>元素CONTA inin
克大小。 最后, the Labe
o the Tesla
Wheels组件内部构建并渲染, the Labe
lItems列表。
In the <lab
el> element, the effect of wheel animation is shown by changing the class according to the transmitted wheel size.
在<lab
el>元素中,通过根据传输的车轮大小更改类来显示车轮动画的效果。
(12.1 TeslaWheels Component Style)
Create a TeslaWheels.css
file in the src/components/TeslaWheels
directory and specify the following styles. Since the code is long and omitted here, let’s check the source code.
在src/components/TeslaWheels
目录中创建TeslaWheels.css
文件,并指定以下样式。 由于代码很长,在这里省略了,让我们检查源代码 。
.tesla-wheels__component { float: left; width: 355px;}.tesla-wheels__title { letter-spacing: 2px; font-size: 16px;}...
(12.2 Import TeslaWheels Component in TeslaBattery Container)
Finally, implement callback
in TeslaBattery
and pass it to the TeslaWheels
component.
最后,在TeslaBattery
实现callback
并将其传递给TeslaWheels
组件。
Add import
to use the TeslaWheels
component in TeslaBattery.js
. We then implement callback function handleChangeWheels()
and bind it in constructor
. Then pass the callback function to the TeslaWheels
component as props
.
添加import
使用TeslaWheels
在组件TeslaBattery.js
。 然后,我们实现回调函数handleChangeWheels()
并将其绑定到constructor
。 然后将回调函数作为props
传递给TeslaWheels
组件。
...import TeslaWheels from '../components/TeslaWheels';...constructor(props) { super(props); this.calculateStats = this.calculateStats.bind(this); this.increment = this.increment.bind(this); this.decrement = this.decrement.bind(this); this.handleChangeClimate = this.handleChangeClimate.bind(this); this.handleChangeWheels = this.handleChangeWheels.bind(this); this.statsUpdate = this.statsUpdate.bind(this);...handleChangeWheels(size) { const config = {...this.state.config}; config['wheels'] = size; this.setState({ config });}...<TeslaWheels value={this.state.config.wheels} handleChangeWheels={this.handleChangeWheels}/>...
The result of the completion of the wheels animation is as follows.
车轮动画的完成结果如下。
(13. State Update)
Are we finally done? Even if the user changes several condition values, the difference value of the vehicle model does not change properly.
我们终于完成了吗? 即使用户改变多个条件值,车辆模型的差值也不会适当地改变。
So far, we’ve only updated a part of our app’s status each time an event occurs.
到目前为止,每次事件发生时,我们仅更新了部分应用程序状态。
this.setState({ config });
Now let’s change the carstats
state whenever the config state value changes.
现在,只要配置状态值更改,就可以更改carstats
状态。
statsUpdate() { const carModels = ['60', '60D', '75', '75D', '90D', 'P100D']; // Fetch model info from BatteryService and calculate then update state this.setState({ carstats: this.calculateStats(carModels, this.state.config) })}
Now we create a function that take the carModels
and the current state value as inputs and reflects the changed carStats
in the app state and pass it to this.setState
as a callback.
现在,我们创建一个函数,将carModels
和当前状态值作为输入,并在应用程序状态中反映更改后的carStats
,并将其作为回调传递给this.setState
。
By doing this, it is possible to update the config
object first in setState()
, which operates asynchronous method, and to render the changed stats
on the screen based on this.
通过这样做,可以首先在操作异步方法的setState()
更新config
对象,并基于此在屏幕上呈现更改的stats
。
this.setState({ config }, () => {this.statsUpdate()});
This completes all the puzzles. The complete code for TeslaBattery is:
这就完成了所有的难题。 TeslaBattery的完整代码是:
import React from 'react';import './TeslaBattery.css';import TeslaNotice from '../components/TeslaNotice/TeslaNotice';import TeslaCar from '../components/TeslaCar/TeslaCar';import TeslaStats from '../components/TeslaStats/TeslaStats';import TeslaCounter from '../components/TeslaCounter/TeslaCounter';import TeslaClimate from '../components/TeslaClimate/TeslaClimate';import TeslaWheels from '../components/TeslaWheels/TeslaWheels';import { getModelData } from '../services/BatteryService';
class TeslaBattery extends React.Component { constructor(props) { super(props);
this.calculateStats = this.calculateStats.bind(this); this.statsUpdate = this.statsUpdate.bind(this); this.increment = this.increment.bind(this); this.decrement = this.decrement.bind(this); this.updateCounterState = this.updateCounterState.bind(this); this.handleChangeClimate = this.handleChangeClimate.bind(this); this.handleChangeWheels = this.handleChangeWheels.bind(this);
this.state = { carstats: [], config: { speed: 55, temperature: 20, climate: true, wheels: 19 } } }
calculateStats = (models, value) => { const dataModels = getModelData(); return models.map(model => { const { speed, temperature, climate, wheels } = value; const miles = dataModels[model][wheels][climate ? 'on' : 'off'].speed[speed][temperature]; return { model, miles }; }); }
statsUpdate() { const carModels = ['60', '60D', '75', '75D', '90D', 'P100D']; // Fetch model info from BatteryService and calculate then update state this.setState({ carstats: this.calculateStats(carModels, this.state.config) }) }
componentDidMount() { this.statsUpdate(); }
updateCounterState(title, newValue) { const config = { ...this.state.config }; // update config state with new value title === 'Speed' ? config['speed'] = newValue : config['temperature'] = newValue; // update our state this.setState({ config }, () => {this.statsUpdate()}); }
increment(e, title) { e.preventDefault(); let currentValue, maxValue, step; const { speed, temperature } = this.props.counterDefaultVal; if (title === 'Speed') { currentValue = this.state.config.speed; maxValue = speed.max; step = speed.step; } else { currentValue = this.state.config.temperature; maxValue = temperature.max; step = temperature.step; }
if (currentValue < maxValue) { const newValue = currentValue + step; this.updateCounterState(title, newValue); } }
decrement(e, title) { e.preventDefault(); let currentValue, minValue, step; const { speed, temperature } = this.props.counterDefaultVal; if (title === 'Speed') { currentValue = this.state.config.speed; minValue = speed.min; step = speed.step; } else { currentValue = this.state.config.temperature; minValue = temperature.min; step = temperature.step; }
if (currentValue > minValue) { const newValue = currentValue - step; this.updateCounterState(title, newValue); } }
// handle aircon & heating click event handler handleChangeClimate() { const config = {...this.state.config}; config['climate'] = !this.state.config.climate; this.setState({ config }, () => {this.statsUpdate()}); }
// handle Wheels click event handler handleChangeWheels(size) { const config = {...this.state.config}; config['wheels'] = size; this.setState({ config }, () => {this.statsUpdate()}); }
render() { const { config, carstats } = this.state; return ( <form className="tesla-battery"> <h1>Range Per Charge</h1> <TeslaCar wheelsize={config.wheels} /> <TeslaStats carstats={carstats} /> <div className="tesla-controls cf"> <TeslaCounter currentValue={this.state.config.speed} initValues={this.props.counterDefaultVal.speed} increment={this.increment} decrement={this.decrement} /> <div className="tesla-climate-container cf"> <TeslaCounter currentValue={this.state.config.temperature} initValues={this.props.counterDefaultVal.temperature} increment={this.increment} decrement={this.decrement} /> <TeslaClimate value={this.state.config.climate} limit={this.state.config.temperature > 10} handleChangeClimate={this.handleChangeClimate} /> </div> <TeslaWheels value={this.state.config.wheels} handleChangeWheels={this.handleChangeWheels} /> </div> <TeslaNotice /> </form> ) }}
export default TeslaBattery;
Check out final project code
查看最终项目代码
(14. Build)
It’s time to build our app.
现在该构建我们的应用程序了。
npm run build
If the build succeeds, the build folder will be created in our project directory and the following message will be displayed.
如果构建成功,将在我们的项目目录中创建构建文件夹,并显示以下消息。
Now our build is ready to be deployed.
现在我们的构建就可以部署了。
(15. Deploy)
With tools like Surge, we can really easily deploy our built app.
使用Surge之类的工具,我们可以真正轻松地部署构建的应用程序。
Surge
is simple, single-command web publishing. It publishes HTML, CSS, and JS for free, without leaving the command line.
Surge
是简单的单命令Web发布。 它免费发布HTML,CSS和JS,而无需离开命令行。
First, install the tool with npm
and run the surge
command in the build
directory.
首先,安装该工具npm
和运行surge
的命令build
目录。
$ npm install -global surge$ cd build$ surge
If this is your first time running, you will need to enter your email and password to register a new account.
如果这是您第一次运行,则需要输入电子邮件和密码来注册新帐户。
The deployment is finished in an instant.
部署立即完成。
Let’s connect to our deployed project.
让我们连接到我们已部署的项目。
react-tesla-charge-calculator.surge.sh
react-tesla-charge-calculator.surge.sh
(Conclusion)
In this post, we learned some points of creating React components and composing them to create a front-end app through rebuilding Tesla's Battery Range Calculator
. If you’ve followed along until now, then congratulations on getting a React app up and running.
在这篇文章中,我们了解了创建React组件并通过重建Tesla's Battery Range Calculator
来构成它们以创建前端应用程序的一些要点。 如果您到目前为止一直都在遵循,那么恭喜您启动并运行了React应用。
In the next installment, we’ll explore how to improve our state management with the Redux
library. In the meantime, if you have any comments, suggestions, or corrections, please feel free to post them in the comments section.
在下一部分中,我们将探索如何使用Redux
库改善状态管理。 同时,如果您有任何评论,建议或更正,请随时在评论部分中发表。
Thanks for your feedback in advance.
感谢您的提前反馈。
翻译自: https://www.freecodecamp.org/news/building-teslas-battery-range-calculator-with-react-part-1-2cb7abd8c1ee/
react中使用构建缓存