by Safeer Hayat
通过更安全的哈亚特
Understanding Redux as a beginner can be quite confusing. Redux has an abundance of new terms and concepts which are often pretty unintuitive. This guide presents a very simplified example of a Redux implementation. I will define each of the steps and terms in a way that makes sense to a complete beginner.
将Redux理解为初学者可能会造成很大的困惑。 Redux有很多新术语和新概念,这些新术语和新概念通常很不直观。 本指南提供了Redux实现的非常简化的示例。 我将以适合初学者的方式定义每个步骤和术语。
This is intended to be a guide to demystify Redux elements. It does not contain the most technically accurate definitions. It does not have the best ever practices. It does have definitions that will help develop an understanding for someone with no prior knowledge of these concepts. There is a simple implementation as to not confuse with unnecessary details.
这旨在作为使Redux元素神秘化的指南。 它不包含技术上最准确的定义。 它没有最佳实践。 它确实具有一些定义,这些定义将有助于对以前不了解这些概念的人有所了解。 有一个简单的实现方式,可以与不必要的细节混淆。
The example we will run through in this guide will be a simple todo app. The app allows a user to add or remove todo items and see them displayed on the page.
我们将在本指南中运行的示例是一个简单的待办应用程序。 该应用程序允许用户添加或删除待办事项,并在页面上显示它们。
I will run through step by step each element of Redux, explaining what that element is and how to implement it with code examples. Scroll to the bottom to see the full code example which will show how it all fits together as a complete React app.
我将逐步介绍Redux的每个元素,说明该元素是什么以及如何通过代码示例实现它。 滚动到底部以查看完整的代码示例,该示例将显示如何将它们全部组合成一个完整的React应用程序。
步骤摘要 (Steps Summary)
- Write the reducer function 编写减速器功能
- Instantiate the store in the root component 在根组件中实例化商店
- Wrap the components with the <Provider> component, passing in the store as a prop 用<Provider>组件包装组件,将其作为道具传入商店
- Write the component 编写组件
- Define the actions 定义动作
- Define the dispatch, attach these to where the dispatches will be triggered (ie event listeners etc) 定义调度,将它们附加到将触发调度的位置(即事件侦听器等)
- Define the mapStateToProps function 定义mapStateToProps函数
- Export the connect function, passing in mapStateToProps and null as the 2 arguments and passing the component name in the second pair of brackets 导出connect函数,传入mapStateToProps和null作为2个参数,并在第二对括号中传递组件名称
脚步 (Steps)
1.编写减速器功能 (1. Write the reducer function)
The reducer function is a function which tells the store how to respond to actions. The function returns the new and updated state whenever an action is dispatched. State is immutable (can’t be changed) so the reducer always returns a new state. The reducer usually uses the spread operator to insert the current state into a new object/array and appending to it. Common practice is to use a switch/case statement and check the type property of the action passed in. Then write the code that updates the state for each case.
减速器功能是告诉商店如何响应动作的功能。 每当分派操作时,该函数都会返回新状态和更新状态。 状态是不可变的(无法更改),因此减速器始终返回新状态。 精简器通常使用散布运算符将当前状态插入到新的对象/数组中并追加到该对象/数组中。 通常的做法是使用switch / case语句并检查传入的操作的type属性。然后编写更新每种情况的状态的代码。
We write our reducer function first because we will need to pass this when we instantiate our store. To understand what’s happening though requires some knowledge of actions and dispatch. We will cover this further on in this guide.
我们首先编写reducer函数,因为在实例化商店时我们将需要传递它。 要了解正在发生的事情,需要对操作和调度有一定的了解。 我们将在本指南中对此进行进一步介绍。
For now know that our todo app will need to interact with the store in 2 ways: to add a new todo item to the state and to remove a todo item from the state. Therefore we write our function so that it responds to 2 cases of the action type. It uses the action value to either add or remove a todo item from the state.
现在,我们的待办事项应用程序将需要通过两种方式与商店交互:将新的待办事项添加到州,并从州中删除待办事项。 因此,我们编写函数以使其能够响应2种情况的动作类型。 它使用操作值在状态中添加或删除待办事项。
The reducer is passed 2 parameters: state (this is the entire state currently in the store, and we give it a default value if state does not exist yet) and the action. We return the state in the default case.
reducer传递了2个参数:状态(这是商店中当前的整个状态,如果状态不存在,则为它提供默认值)和操作。 我们以默认情况返回状态。
2.实例化根组件中的存储 (2. Instantiate the store in the root component)
The store is the thing which actually contains the state in it. It’s a bit magical and you don’t really need to know the ins and outs of it. All you need to know is that you don’t access it directly like you would a normal React state. You access it and make changes to it using reducers, actions and dispatch.
商店实际上就是其中包含状态的事物。 这有点神奇,您实际上不需要了解它的来龙去脉。 您需要知道的是,您不会像正常的React状态那样直接访问它。 您可以访问它并使用化简器,操作和调度对其进行更改。
The other important thing to know about the store is that it contains some useful and important methods. The main method is the dispatch function. It also contains a getState method (for viewing the state) and subscribe method (runs a callback every time an action is dispatched).
关于商店的另一件重要的事情是它包含一些有用且重要的方法。 主要方法是调度功能。 它还包含getState方法(用于查看状态)和subscription方法(每次分派操作时运行回调)。
The store is typically instantiated at the root of your app (e.g. App.js). It is stored as a variable and has the reducer passed in as a parameter. The store is then passed in as a prop to the Provider component.
该商店通常在应用程序的根目录(例如App.js)上实例化。 它存储为变量,并将减速器作为参数传递。 然后将商店作为道具传递给提供程序组件。
We instantiate our store object passing in the reducer we just created.
我们实例化传入刚刚创建的化简器的商店对象。
3.用<Provider>组件包装组件,将其作为道具传入商店 (3. Wrap the components with the <Provider> component, passing in the store as a prop)
The Provider is a component created to make it easier to pass the store to all your components. The Provider component wraps around all your components (e.g. render your components as children of Provider). You pass the store in as a prop to the Provider only. This means you don’t need to pass in the store as a prop to every component as each component gets it from the Provider. However, this doesn’t mean the components have access to the state yet. You still need to use the mapStateToProps (we will cover this later) to have the state accessible in your component.
提供程序是一个创建的组件,它使将商店传递给所有组件变得更加容易。 Provider组件包装了所有组件(例如,将组件渲染为Provider的子代)。 您只能将商店作为道具传递给提供者。 这意味着您不需要将存储作为每个组件的道具传递,因为每个组件都可以从提供者那里获取。 但是,这并不意味着组件还可以访问状态。 您仍然需要使用mapStateToProps(我们将在以后进行介绍)以使状态可在组件中访问。
We wrap the Todo component we are going to make with our Provider component. We pass in the store we created in the previous step.
我们使用Provider组件包装要制作的Todo组件。 我们传入在上一步中创建的商店。
4.编写组件 (4. Write the component)
Next, we begin to write the Todo component which will render the todo items and interact with the Redux store.
接下来,我们开始编写Todo组件,该组件将渲染待办事项并与Redux商店进行交互。
The component is a stateful component containing one state element to keep track of what the user has typed into the input. We have a function called handleChange. This function updates the state every time the user types anything into the input. So far this is all we will write. We need to understand more about Redux before we can write the logic. The logic will add new todos to the state and retrieve current ones from the state to render on the page.
该组件是一个有状态的组件,其中包含一个状态元素,以跟踪用户在输入中键入了什么。 我们有一个称为handleChange的函数。 每当用户在输入中输入任何内容时,此功能都会更新状态。 到目前为止,这就是我们要写的全部内容。 在编写逻辑之前,我们需要了解有关Redux的更多信息。 逻辑将向状态添加新的待办事项,并从状态中检索当前待办事项以呈现在页面上。
5.定义动作 (5. Define the actions)
An action is a simple object containing a property called ‘type’. This object is passed into the dispatch function. It is used to tell the store what event has just occurred (by reading the actions type property). It also tells what update it should make to the state in response (through the reducer function). The action can also contain other properties for any other data you want to pass into the reducer. Data can only be passed through here so any data needed will need to be passed in here.
动作是一个简单的对象,其中包含一个称为“类型”的属性。 该对象传递到调度函数中。 它用于告诉商店刚刚发生了什么事件(通过读取action type属性)。 它还告诉响应状态(通过reducer函数)应对状态进行哪些更新。 该操作还可以包含要传递给化简器的任何其他数据的其他属性。 数据只能在此处传递,因此所需的任何数据都需要在此处传递。
We will use action creators to define our actions. Action creators are a function which returns the action object. Its purpose is to make the action more portable and testable. It doesn’t change the behavior of how anything works. It’s another method of writing and passing the action. It also allows you to pass in parameters if you want to send data with the action which we will be doing. So we require to use action creators here.
我们将使用动作创建者来定义我们的动作。 动作创建者是一个返回动作对象的函数。 其目的是使操作更具可移植性和可测试性。 它不会改变任何事物的行为。 这是编写和传递动作的另一种方法。 如果您想通过我们将要执行的操作发送数据,它还允许您传递参数。 因此,我们需要在此处使用动作创建者。
If you remember our reducer responded to 2 action types — “ADD_TODO” and “REMOVE_TODO”. We will define those actions with our action creators. In our add_todo action will return “ADD_TODO” as the type and the todo item we want to add to the store as the value (we need the store to add this todo item to the state so it gets passed in here). In the remove_todo we return “REMOVE_TODO” as the type and the index of the todo item in the store as the value. We’ll need this to remove it from the list of todos.
如果您还记得,我们的减速器对两种动作类型进行了响应-“ ADD_TODO”和“ REMOVE_TODO”。 我们将与动作创建者一起定义这些动作。 在我们的add_todo操作中,将返回“ ADD_TODO”作为类型,并返回要添加到存储中的待办事项作为值(我们需要商店将此待办事项添加到状态,以便它在此处传递)。 在remove_todo中,我们返回“ REMOVE_TODO”作为类型,并返回商店中待办事项的索引作为值。 我们需要此功能将其从待办事项列表中删除。
If you return to our reducer function definition hopefully it now makes more sense. By reading the action.type the reducer knows whether it needs to add a todo to the state or remove one from it. It has the todo item passed in the add_todo. It appends to the current state using the spread operator. In the remove_todo it uses the spread operator to create a new array appending the current state sliced twice, once with all the elements before the one to remove and second with all the elements after the one to remove, thus creating our new state object with the todo item removed.
如果您希望返回我们的化简函数定义,那么现在更有意义了。 通过读取action.type,reducer知道它是否需要向状态添加待办事项或从状态中删除一个待办事项。 它在add_todo中传递了待办事项。 使用扩展运算符将其附加到当前状态。 在remove_todo中,它使用spread运算符创建一个新的数组,该数组将切成两次的当前状态附加到切片中,一次删除所有元素,然后删除所有元素,从而创建一个新的状态对象,其中待办事项已删除。
However, this still isn’t a complete picture. We have not yet covered how the reducer gets called and passed in the right action. For that, we will need to move on to define our dispatch function.
但是,这还不是完整的图片。 我们尚未介绍在正确的操作中如何调用和传递reducer。 为此,我们将继续定义调度功能。
6.定义调度,将它们附加到将触发调度的位置(即事件侦听器等) (6. Define the dispatch, attach these to where the dispatches will be triggered (ie event listeners etc))
The dispatch function is a method of the store which is used to trigger a change in the state. Any event or anything which needs to update the state must call the dispatch method to do so. This is the only way to trigger a change/update to the state. Dispatch is called and the action object is passed in (or the action creator if that was used). Once a dispatch is triggered the store then calls the reducer function and passes in the action that the dispatch provided which updates the state, as we’ve seen earlier.
调度功能是存储的一种方法,用于触发状态更改。 任何事件或任何需要更新状态的事物都必须调用dispatch方法来执行。 这是触发状态更改/更新的唯一方法。 调用Dispatch并传递操作对象(或使用操作创建者)。 一旦触发了调度,商店便会调用reducer函数,并传递调度所提供的操作来更新状态,如前所述。
Below we define the bottom half of our Components render method. We create our buttons which will contain our event handlers. Inside those, we will define our dispatch functions.
在下面,我们定义了组件渲染方法的下半部分。 我们创建包含事件处理程序的按钮。 在这些内部,我们将定义我们的调度功能。
The first button is a simple add button. This button will dispatch the add_todo action to the store. It will pass in the current user input as the value (this is the todo item that the reducer appends to the new state). Note we call dispatch as this.props.dispatch
. It’s a bit out of the scope of this guide to understand how and why this gets passed as a prop to the component. So just know that it does and we can call it like this.
第一个按钮是一个简单的添加按钮。 此按钮将将add_todo操作调度到商店。 它将作为值传递当前用户输入(这是化简器附加到新状态的待办事项)。 注意,我们将调度称为this.props.dispatch
。 了解如何以及为什么将它作为道具传递给组件超出了本指南的范围。 因此,只要知道它确实可以,我们就可以这样称呼它。
The second event handler is written as an onClick on our rendered todo item. By clicking on any todo item on the page it triggers an event handler. The event handler searches the list of todos and finds the index of that todo in the list. It then dispatches the remove_todo action and passes in the index.
第二个事件处理程序被编写为渲染的待办事项上的onClick。 通过单击页面上的任何待办事项,它会触发事件处理程序。 事件处理程序搜索待办事项列表,并在列表中找到该待办事项的索引。 然后,它调度remove_todo操作并传递索引。
The cycle for how to update the state in the Redux store is now fully defined. We know that any time we want to change the state we need to call the dispatch method, pass in the appropriate action, and ensure our reducer handles those actions and returns the new state using any values we passed in via the action.
现在已完全定义了如何在Redux存储中更新状态的周期。 我们知道,每当要更改状态时,我们都需要调用dispatch方法,传递适当的操作,并确保我们的reducer处理这些操作并使用通过操作传递的任何值返回新状态。
The only puzzle piece missing now is how do we get the state from the Redux store. You’ve probably noticed that I’ve mapped over a list called this.props.todos
in the previous example. You may be wondering where that came from. You may also recall at the beginning of this guide I mentioned that passing store into the Provider component is not enough to gain access to the state in the store. This is all addressed in the next 2 steps as we define our mapStateToProps function and pass that into the connect function.
现在唯一缺少的难题是如何从Redux商店获取状态。 您可能已经注意到,在上一个示例中,我已映射到名为this.props.todos
的列表上。 您可能想知道那是哪里来的。 您可能还记得在本指南的开头我提到过,将存储传递到Provider组件不足以获取对存储中状态的访问。 在定义我们的mapStateToProps函数并将其传递给connect函数时,将在接下来的2个步骤中解决所有问题。
7.定义mapStateToProps函数 (7. Define the mapStateToProps function)
When you want your component to have access to the state you have to explicitly specify what in the state the component will get access to. Your component will not have access to state without this.
当您希望组件可以访问状态时,必须明确指定组件可以访问的状态。 否则,您的组件将无法访问状态。
mapStateToProps is a function which simply returns an object that defines what state should be passed into the component by assigning values in the state to properties you define in this object. Essentially, the object you return in the mapStateToProps is what your props will be in your component. The mapStateToProps function is passed into the connect method as the first argument.
mapStateToProps是一个函数,它简单地返回一个对象,该对象通过将状态中的值分配给在此对象中定义的属性来定义应将什么状态传递到组件中。 本质上,您在mapStateToProps中返回的对象就是您的prop将在组件中的内容。 mapStateToProps函数作为第一个参数传递到connect方法中。
The mapStateToProps takes the entire state as a parameter and you take only what you need from it. Here though as our state only contains the list of todos. We need that list in our ToDo component, we will return the entire state as a property called todos.
mapStateToProps将整个状态作为参数,而您只需要它所需要的。 尽管这里我们的州只包含待办事项清单。 我们需要在ToDo组件中使用该列表,然后将整个状态作为名为todos的属性返回。
As you can see now, we have access to our entire todos list in our props as this.props.todos
. This is how we were able to render all our todos in the previous example by mapping over it.
如您现在所见,我们可以在道具中以this.props.todos
访问整个this.props.todos
列表。 这就是我们能够通过映射上一个示例来渲染所有待办事项的方式。
Finally we need to pass this function into our connect method to connect everything together.
最后,我们需要将此函数传递到connect方法中,以将所有内容连接在一起。
8.导出connect函数,传入mapStateToProps和null作为2个参数,并在第二对括号中传递组件名称 (8. Export the connect function, passing in mapStateToProps and null as the 2 arguments and passing the component name in the second pair of brackets)
Connect is a method that hooks up mapStateToProps and mapDispatchToProps (see below) functions to your component so that the store can read those functions and ensure what you defined in there gets passed into the component as props. This method has a special syntax which looks like this:
Connect是一种将mapStateToProps和mapDispatchToProps函数(请参见下文)连接到您的组件的方法,以便商店可以读取这些函数并确保您在其中定义的内容作为prop传递到组件中。 此方法具有特殊的语法,如下所示:
connect(mapStateToProps, MapDispatchToProps)(YourComponent)
connect(mapStateToProps, MapDispatchToProps)(YourComponent)
You pass in the 2 map...ToProps
functions to the connect and then the name of your component inside the second pair of brackets. A typical pattern is to export the connect method instead of your component when you are exporting your component at the end of your file. For example:
您将2 map...ToProps
函数传递给connect,然后在第二对括号内传递组件名称。 一种典型的模式是在文件末尾导出组件时,导出connect方法而不是组件。 例如:
export default connect(mapStateToProps, MapDispatchToProps)(YourComponent)
export default connect(mapStateToProps, MapDispatchToProps)(YourComponent)
This then acts in the same way as exporting normally except the state and dispatches will be passed in as props. mapStateToProps and mapDispatchToProps are actually optional params to connect. If you don’t want to pass one or either, put null in their place instead.
然后,它的行为与正常导出的行为相同,除了状态和调度将作为道具传递。 mapStateToProps和mapDispatchToProps实际上是要连接的可选参数。 如果您不想传递一个或两个,则将null代替。
You may be wondering where this mapDispatchToProps function has come from and why we haven’t mentioned it anywhere before here. Well, as this guide is the most simplified example of a Redux store and mapDispatchToProps isn’t strictly mandatory, I haven’t included it in our example. If you don’t pass mapDispatchToProps and pass null instead then you can still access the dispatch function in your component as we have earlier as this.props.dispatch
.
您可能想知道mapDispatchToProps函数的来源以及为什么我们在此之前未提及它。 好吧,由于本指南是Redux存储的最简化示例,并且mapDispatchToProps不是严格必需的,因此我没有在示例中包含它。 如果您不传递mapDispatchToProps并传递null,那么您仍然可以访问组件中的dispatch函数,就像我们之前的this.props.dispatch
。
So to finish off our example app, all we have to do is export our component wrapping it with the connect function and passing in the mapStateToProps we just defined.
因此,要完成示例应用程序,我们要做的就是导出使用connect函数将其包装的组件,并传入刚刚定义的mapStateToProps。
And that’s it! That’s a complete implementation of a Redux store. See below for the working example of what we implemented.
就是这样! 这是Redux存储的完整实现。 请参阅以下有关我们实施的工作示例。
完整注释的代码示例 (Full Annotated Code Example)
App.js
App.js
Todo.js
Todo.js
I hope that this guide can simplify some of the strange and sometimes confusing details of Redux. It’s not a complete guide of Redux, as there are definitely more elements and patterns to understand. But if you can understand this guide then you are well on your way to being able to work with and install Redux in your apps.
我希望本指南可以简化Redux的一些奇怪的,有时令人困惑的细节。 它不是Redux的完整指南,因为肯定还有更多的元素和模式需要理解。 但是,如果您能理解本指南,那么您就可以很好地使用Redux并在您的应用程序中安装Redux。
翻译自: https://www.freecodecamp.org/news/a-beginners-guide-to-redux-9f652cbdc519/