Redux is an important part of the React Native ecosystem. If your world revolves around JavaScript, you’ve probably heard about Redux. Before reading the rest of the tutorial and going further, just try to remember that you are only learning about Redux because it will make things easier for you, and not more difficult. Now let us learn why you need Redux in your application.
Redux是React Native生态系统的重要组成部分。 如果您的世界围绕着JavaScript旋转,那么您可能已经听说过Redux。 在阅读本教程的其余部分并继续之前,请记住,您只是在学习Redux,因为它会使您更轻松,而不是更困难。 现在让我们了解为什么您的应用程序需要Redux。
极品飞车 (Need for Redux)
Building a React or React Native application in the real world can become complex if there is not a proper way to handle data. If at any point the data is not managed, things will get out of hand. If you are familiar with React or React Native, you know the default way of handling data is to keep it in a component state and pass it to children components as props.
如果没有适当的数据处理方式,那么在现实世界中构建React或React Native应用程序可能会变得很复杂。 如果在任何时候都无法管理数据,那么事情将一发不可收拾。 如果您熟悉React或React Native,则知道处理数据的默认方法是将其保持在组件状态并将其作为道具传递给子组件。
State and Props are the only two ways to control data in a component. Props is short for properties. It is a simple rule to follow in the React world that we should not mutate or change the value of props. In React, the flow of data is unidirectional or one way. That is, the data can always be passed from a parent to a child component. Take a look below at this simple example:
状态和道具是控制组件中数据的仅有两种方法。 道具是财产的简称。 在React世界中遵循一个简单的规则,即我们不应变异或更改道具的价值。 在React中,数据流是单向或单向的。 也就是说,数据始终可以从父组件传递到子组件。 下面看一下这个简单的例子:
In the above example, we create two components (Parent and Child) in separate files. The Parent component consists of a view where the Child component is rendered. In the child component, the view renders a text message that is incoming from the props. The incoming message is available as the data in the state of the parent component.
在上面的示例中,我们在单独的文件中创建了两个组件(父级和子级)。 父级组件包含一个呈现子级组件的视图。 在子组件中,视图呈现从道具传入的文本消息。 传入消息可用作父组件状态下的数据。
This way, the child component can be reused with other parent components such that each parent component can have its own data to render. Do note that we are not modifying the value of this.props
at any point.
这样,子组件可以与其他父组件一起重用,以便每个父组件可以拥有自己的数据来呈现。 请注意,我们在任何时候都不会修改this.props
的值。
The state is there to mutate data. This is the only reason that the state exists within each component. Whenever we want to change the state, we use this.setState()
method within a component. This method re-renders the component and all of its child components to reflect the changes. This works both in React and React Native similarly, but the internals are different.
该状态用于改变数据。 这是每个组件中都存在状态的唯一原因。 每当我们想要更改状态时,我们都在组件内使用this.setState()
方法。 此方法重新呈现组件及其所有子组件以反映更改。 这在React和React Native中都类似地起作用,但是内部原理不同。
Since we can manage state and props so efficiently within a React Native app, why is Redux necessary? Well, the above example represents the bare minimum and not a real-time scenario. Imagine an application like Instagram or Twitter. You have different screens, and each screen may depend on a component or two like the Parent and the reusable Child components from our example. It would be hard to keep track of the state of each component.
由于我们可以在React Native应用程序中如此高效地管理状态和道具,为什么需要Redux? 好吧,上面的示例代表了最低限度,而不是实时方案。 想象一个像Instagram或Twitter这样的应用程序。 您有不同的屏幕,每个屏幕可能取决于一个或两个组件,例如我们示例中的Parent和可重用Child组件。 很难跟踪每个组件的状态。
Redux is one the most widely adopted ways of handling data. It enables the state to be shared as a global attribute that an entire React Native application can use and receive in the form of props. This is known as creating a store in Redux. Redux simplifies the state by moving it into one place.
Redux是处理数据的最广泛采用的方法之一。 它使状态可以作为全局属性共享,整个React Native应用程序可以以props形式使用和接收该状态。 这被称为在Redux中创建商店。 Redux通过将状态移动到一个位置来简化状态。
Redux uses an underlying React Mechanism called context. We are not going to dwell what context is, since it is out of the scope of this article. I just wanted you to know that nothing magical is happening behind the scenes.
Redux使用称为上下文的底层React机制。 我们将不讨论上下文是什么,因为它不在本文的讨论范围之内。 我只是想让您知道幕后没有发生任何神奇的事情。
Just remember the following terms, since we are going to see them in action in the tutorial below:
请记住以下术语,因为我们将在下面的教程中看到它们的作用:
- Actions 动作
- Reducers 减速器
- Store 商店
The key to learning Redux is practice. I don’t want to share too much information and overwhelm things right now. So let us begin by creating a demo application to learn Redux.
学习Redux的关键是练习。 我现在不想分享太多信息并压倒一切。 因此,让我们开始创建一个演示应用程序来学习Redux。
构建一个Pomodoro应用程序 (Building a Pomodoro application)
Expo-CLI入门? (Getting Started with Expo-CLI ?)
To build this application, I am going to use the latest tool introduced by the Expo team called expo-cli. Install it as a global dependency and then initialise a new React Native project using it.
为了构建该应用程序,我将使用由Expo团队引入的最新工具expo-cli 。 将其安装为全局依赖项,然后使用它初始化一个新的React Native项目。
To see if everything is working correctly at this initial state, run the following command.
要查看在此初始状态下一切是否正常运行,请运行以下命令。
You will be prompted with the following interface. Take some time to go through it. If you have build applications using Expo XDE or Create-React-Native-App before, you will see that not much has changed, except that now Expo-CLI makes use of the Chrome browser.
系统将提示您以下界面。 花一些时间来完成它。 如果您以前使用Expo XDE或Create-React-Native-App构建应用程序,您将看到的变化不大,只是现在Expo-CLI使用了Chrome浏览器。
Choose a simulator or device that can run Expo Client as marked in the above image. If you get the below screen, that means our React Native project has been initialised without any difficulties.
如上图所示,选择可以运行Expo Client的模拟器或设备。 如果您看到以下屏幕,则意味着我们的React Native项目已经初始化,没有任何困难。
With that, create the following files and folders inside the components
directory. I will discuss why we are following this directory structure later. For now, our initial setup is complete and we can start building our application.
这样,在components
目录内创建以下文件和文件夹。 稍后我将讨论为什么我们遵循此目录结构。 目前,我们的初始设置已完成,我们可以开始构建应用程序。
计时器组件⏱ (Timer Component ⏱)
First, we will create a dumb Timer component and connect it with App.js
. Add the following code to the Timer/index.js
:
首先,我们将创建一个哑计时器组件并将其与App.js
连接。 将以下代码添加到Timer/index.js
:
Next, modify the App.js
file:
接下来,修改App.js
文件:
We will now make a static Timer component to see how things fit in. We’ll start by modifying the StatusBar
. Then we define two Text
elements from the react-native
library to specify where the actual timer will be displayed and where the buttons for starting and stopping the timer will be displayed. For now, both are text fields.
现在,我们将创建一个静态Timer组件,以查看其功能。首先,我们将修改StatusBar
。 然后,我们从react-native
库中定义两个Text
元素,以指定实际计时器将显示在何处以及计时器的启动和停止按钮将在何处显示。 目前,这两个都是文本字段。
添加按钮? (Adding Buttons ?)
In this section, we are going to replace the section that displays Start and Stop Buttons!
with actual buttons. We will be using TouchableOpactiy
to make this work. A TouchableOpacity
component acts as a wrapper for making views respond properly to touches. The opacity of the wrapped view (or the button in our case) gets decreased whenever a user touches it.
在本节中,我们将替换显示“ Start and Stop Buttons!
与实际的按钮。 我们将使用TouchableOpactiy
来完成这项工作。 TouchableOpacity
组件充当包装,使视图对触摸正确响应。 每当用户触摸时,包装视图(或本例中的按钮)的不透明度就会降低。
We create a reusable component since we need two buttons: Start and Stop.
因为需要两个按钮,所以我们创建了一个可重用的组件:“开始”和“停止”。
This is a stateless component, so it has no class — we only need it to represent the Button in the UI of our app. We also import FontAwesome icons from @expo/vector-icons
, which is a fork of react-native-vector-icons and comes directly with the expo SDK. No need to install it as a separate dependency. To display an icon, we need to define its size
.
这是一个无状态组件,因此没有类-我们只需要它来表示应用程序UI中的Button。 我们还从@expo/vector-icons
导入FontAwesome图标,这是react-native-vector-icons的一个分支,直接与expo SDK一起提供。 无需将其作为单独的依赖项安装。 要显示图标,我们需要定义其size
。
Lastly, in the above stateless component, we define propTypes
. I will be discussing how and why we should use PropTypes in a React Native application in another article.
最后,在上述无状态组件中,我们定义了propTypes
。 我将在另一篇文章中讨论如何以及为什么我们应该在React Native应用程序中使用PropTypes。
In a mobile app, events are triggered by touch. To handle those events, we are going to use onPress
. We will have only two events here, Start and Stop. Both the buttons in our app are going to make use of onPressOut
which differs from onPress
. The onPressOut
is called whenever the touch is released by the user (when the user stops pressing the button). It is called before onPress
and is more accurate in a situation like ours where we need to start or stop the timer by pressing the button as soon as the user is done.
在移动应用中,事件是通过触摸触发的。 为了处理这些事件,我们将使用onPress
。 我们这里只有两个事件,开始和停止。 我们应用中的两个按钮都将使用与onPress
不同的onPressOut
。 每当用户释放触摸时(当用户停止按下按钮时),就会调用onPressOut
。 它在onPress
之前调用,在像我们这样的情况下更为准确,在这种情况下,我们需要在用户完成操作后立即通过按下按钮来启动或停止计时器。
We will now require this Button
component in our Timer component.
现在,我们将在Timer组件中需要此Button
组件。
集成Redux? (Integrating Redux ?)
So far, our Timer application does not do anything other than display a bare minimum UI. To make it work, we start by adding some necessary Redux dependencies.
到目前为止,我们的Timer应用程序除了显示最低限度的UI之外没有执行任何其他操作。 为了使其工作,我们首先添加一些必要的Redux依赖项。
Now, let us start integrating Redux in our app.
现在,让我们开始将Redux集成到我们的应用程序中。
动作? (Actions ?)
In Redux, the state of the whole application is represented by one JavaScript object. Think of this object as read-only, since we cannot make changes to this state (which is represented in the form of a tree) directly. We need actions
to do so.
在Redux中,整个应用程序的状态由一个JavaScript对象表示。 将此对象视为只读对象,因为我们无法直接更改此状态(以树的形式表示)。 我们需要actions
来做到这一点。
Actions are like events in Redux. They can be triggered in the form of mouse clicks, key presses, timers or network requests. The nature of each event mentioned is mutable. An action is a JavaScript object. To define an action, there is one requirement: each action much have its own type property. We define these types in a file called types.js
:
动作就像Redux中的事件。 它们可以通过单击鼠标,按键,计时器或网络请求的形式来触发。 提到的每个事件的性质都是可变的。 动作是一个JavaScript对象。 要定义一个动作,有一个要求:每个动作都具有自己的type属性。 我们在一个名为types.js
的文件中定义这些类型:
Our application needs only three actions so far. The type of any action is a string value and is defined as a constant.
到目前为止,我们的应用程序仅需要执行三个操作。 任何操作的类型都是字符串值,并定义为常量。
In the file actions.js
, we will require these types to define action creators. Action Creators are functions that create actions.
在actions.js
文件中,我们将需要这些类型来定义动作创建者。 动作创建者是创建动作的功能。
减速器? (Reducers ?)
The receiver of the action is known as a reducer. Whenever an action is triggered, the state of the application changes. The handling of the application’s state is done by the reducers.
动作的接收者称为减速器。 每当触发操作时,应用程序的状态都会更改。 应用程序状态的处理由reducers完成。
A reducer is a pure function that calculates the next state based on the initial or previous state. It always produces the same output if the state is unchanged. It takes two inputs, and state and action must return the default state.
约简器是一种纯函数,可以根据初始状态或先前状态计算下一个状态。 如果状态不变,它将始终产生相同的输出。 它需要两个输入,状态和操作必须返回默认状态。
In our initial state, we define three attributes: isPlaying
, elapsedTime
and timerDuration
. The timer currently has a default value of 6 (seconds) for testing purposes, but the actual value we are going to change later is 25
(or 1500 seconds).
在初始状态下,我们定义三个属性: isPlaying
, elapsedTime
和timerDuration
。 目前,出于测试目的,计时器的默认值为6(秒),但我们稍后要更改的实际值为25
(或1500秒)。
Then there are three helper functions:
然后有三个帮助器功能:
applyStartTimer
will start the timerapplyStartTimer
将启动计时器applyRestartTimer
will stop the timer function and set everything to defaultapplyRestartTimer
将停止计时器功能并将所有内容设置为默认值and lastly,
applyAddSecond
will check if the time passed is less than the total timer's duration. If so, it’ll add one more second to increase its value. If the not, it will return the default state and stop the timer function from running.最后,
applyAddSecond
将检查经过的时间是否小于计时器的总持续时间。 如果是这样,它将再增加一秒钟以增加其价值。 如果不是,它将返回默认状态并停止计时器功能的运行。
After that, we define our reducer function and export the same function. Observe how the reducer function is organised. This is a pattern followed by most community members I have seen on the internet.
之后,我们定义化简函数并导出相同的函数。 观察减速器功能的组织方式。 我在互联网上看到的大多数社区成员都遵循这种模式。
This is a good resource to get started with Redux in general by Dan Abramov and it is FREE!
这是Dan Abramov全面入门Redux的好资源,它是免费的!
创建Redux Store? (Creating Redux Store ?)
With the help of the reducer and the initial state, we can create the store object.
借助reducer和初始状态,我们可以创建store对象。
A store is an object that brings and actions and reducers together. It provides and holds state at the application level instead of individual components. Redux is not an opinionated library in terms of which framework or library should use it or not.
商店是将行动与减速器集合在一起的对象。 它在应用程序级别而不是单个组件上提供并保持状态。 就应该使用哪个框架或库而言,Redux并不是一个自以为是的库。
To bind a React or React Native application with Redux, you do it with react-redux
module. This is done by using the high ordered component Provider
. It basically passes the store down to the rest of the application.
要将React或React Native应用程序与Redux绑定,请使用react-redux
模块进行。 这是通过使用高阶组件Provider
。 基本上,它将存储传递到应用程序的其余部分。
We need to bind action creators with our Timer function in order to make it fully functional (so that it responds to the touchable events or the start or restart of the timer). We will do this in the Timer/index.js
function.
我们需要将动作创建者与Timer函数绑定在一起,以使其完全发挥作用(以便它响应可触摸的事件或计时器的启动或重启)。 我们将在Timer/index.js
函数中执行此操作。
First, we import the required dependencies to bind action creators.
首先,我们导入所需的依赖关系以绑定动作创建者。
bindActionCreators
maps action functions to an object using the names of the action functions. These functions automatically dispatch the action to the store when the function is called. To change the data, we need to dispatch an action. To enable this, we need two things: mapStateToProps
and mapDispatchToProps
, and we need to connect both of them with our component. This is the boilerplate code that you will be re-writing.
bindActionCreators
使用动作函数的名称将动作函数映射到对象。 调用该函数时,这些函数自动将操作分派到商店。 要更改数据,我们需要调度一个动作。 为了实现这一点,我们需要两件事: mapStateToProps
和mapDispatchToProps
,我们需要将它们与我们的组件连接起来。 这是您将要重写的样板代码。
We define these two functions and modify our export default
statement after we define the styles for our React Native views.
我们定义这两个函数并在定义React Native视图的样式后修改export default
语句。
mapStateToProps
is an object that lives in the store whose keys are passed down to the component as props. The below is the complete code for the Timer component.
mapStateToProps
是一个位于商店中的对象,其密钥作为props向下传递到组件。 以下是Timer组件的完整代码。
完成App⚛️+吗? (Completing The App ⚛️ + ?)
I have created a custom function called formatTime
to display the time in the correct format, but you can make use of any timer library. Next, to increment the value of time, I use the React lifecycle method componentWillReceiveProps
. I know it is going to deprecated soon, but for now it works. See our mini-app in action below:
我创建了一个名为formatTime
的自定义函数,以正确的格式显示时间,但是您可以使用任何计时器库。 接下来,要增加时间值,我使用React生命周期方法componentWillReceiveProps
。 我知道它很快就会过时,但是现在可以了。 请在下面查看我们的迷你应用程序:
For the sake of brevity and this demo, I am using only seconds to display the timer. You can increase the value of the timer by editing the value of constant TIMER_DURATION
in reducers.js
.
为了简洁起见和本演示,我仅用几秒钟来显示计时器。 您可以通过在reducers.js
编辑常量TIMER_DURATION
的值来增加计时器的值。
We have reached the end of the article. Hopefully, you have had as much fun reading it as I did writing it. You can find the complete code for this article at this Github repo:
我们到了本文的结尾。 希望您阅读时和我编写时一样开心。 您可以在以下Github存储库中找到本文的完整代码:
amandeepmittal/rn-pomodoro-examplern-pomodoro-example - React Native + Redux integarationgithub.com
amandeepmittal / rn-pomodoro-example rn-pomodoro-example-React Native + Redux整合 github.com
Do you remember me telling you about a particular file structure I followed in implementing Redux architecture? Well it is called re-ducks pattern and you can find more details in this informative article by Alex Moldovan:
您还记得我告诉过您有关实施Redux架构时遵循的特定文件结构吗? 好吧,这就是所谓的“鸭子模式”,您可以在Alex Moldovan的这篇内容丰富的文章中找到更多详细信息:
Scaling your Redux App with ducksHow does your front-end application scale? How do you make sure that the code you’re writing is maintainable 6 months…medium.freecodecamp.org
用鸭子来扩展Redux应用程序 前端应用程序如何扩展? 您如何确保所编写的代码可维护6个月…… medium.freecodecamp.org
? For more questions, contact me on Twitter, or read more about me at my website.
? 如有其他问题,请在Twitter上与我联系,或在我的网站上阅读有关我的更多信息。
如果您想收到我的下一篇关于React Native的文章的更新,请考虑注册我的新闻通讯。