漫长的未来 (A Long Time Coming)
I feel like this blog has been in the making for a few weeks now. Redux as a state management tool makes so much sense as apps get larger and just seemed like the logical next step in my React tool belt. Redux at a high-level is very easy to understand and implementing it is a lot easier than I thought. After making a few components and connecting them to your store it really becomes muscle memory. However using it is going to make your state management life improve ten fold.
我觉得这个博客已经发展了几个星期。 Redux作为一种状态管理工具,随着应用程序的变大而变得非常有意义,这似乎是我的React工具带中合乎逻辑的下一步。 高级别的Redux非常容易理解,比我想象的要容易得多。 制作完一些组件并将它们连接到商店后,它实际上成为了肌肉记忆。 但是,使用它会使您的状态管理寿命提高十倍。
定义 (Define)
In the word of the Redux team it’s “A Predictable State Container for JS Apps”. Essentially you remove the storage of state from your React components and keep everything in one central store
that exists outside of the component hierarchy. You connect components to pieces of state they need through accessing this store
and passing the state as props to the component. You can also make changes to your store from inside the component when you need to make a change. We do this using dispatch
and a reducer
.
用Redux团队的话来说就是“ JS应用程序的可预测状态容器”。 本质上,您从React组件中删除了状态存储,并将所有内容都保留在组件层次结构之外的一个中央store
中。 通过访问此store
并将状态作为道具传递到组件,可以将组件连接到它们所需的状态。 您还可以在需要进行更改时从组件内部对商店进行更改。 我们使用dispatch
和reducer
进行此操作。
安装 (Installation)
React-redux is just like any other package and is installed using your package manager of choice. You can add it to your project with npm install react-redux
or yarn add react-redux
. After installed you will need to do several things before you can start thinking about getting state in your components from the Redux store. You’ll need to do the same with the redux package npm install redux
.
React-redux与其他任何软件包一样,都使用您选择的软件包管理器进行安装。 您可以使用npm install react-redux
或yarn add react-redux
将其添加到项目中。 安装后,您需要做几件事,然后才能开始考虑从Redux存储获取组件中的状态。 您需要对redux软件包npm install redux
。
使用提供程序包装应用程序并创建商店 (Wrap the App with Provider and Create the Store)
At the top level of your application you need to wrap your App component with the Redux Provider
. This will give your entire app access to the store ( don’t worry we’re about to talk about that ). The provider is imported at the top of the index.js
file. It’s here in index.js
that we will create our store. Redux does this through the createStore
function. This is imported in the same way as the Provider
was from react-redux
. We’re going to pass the store we create to the Provider
as a prop The createStore
function takes at least one argument, a reducer. Which is where your initial state object, and list of dispatch actions is going to live. See below for the sample index.js
file. This snippet assumes you’ve made a reducer.js
file on the same level as index.js
. We will take care of that next.
在应用程序的顶层,您需要使用Redux Provider
包装您的App组件。 这将使您的整个应用程序都可以访问商店(不用担心,我们将在此进行讨论)。 该提供程序将导入到index.js
文件的顶部。 我们将在index.js
中创建商店。 Redux通过createStore
函数来做到这一点。 导入方式与Provider
从react-redux
。 我们要通过我们建立的商店Provider
的道具的createStore
功能至少需要一个参数,一个减速。 您的初始状态对象和调度动作列表将位于其中。 请参阅下面的示例index.js
文件。 此代码段假定您已在与index.js
相同级别上创建了reducer.js
文件。 接下来,我们将处理。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import {reducer} from './reducer'
let store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider >,
document.getElementById('root')
);
创建一个Reducer文件 (Create a Reducer File)
The reducer is where a lot of the action is going to happen when it comes to making changes to your store. In its very basic form a reducer could just return your initialState. Once we have the store connected to some components we are going to start changing that pretty fast. A simple reducer file is going to be made up of only a couple of things.
在更改商店时,reducer是很多动作发生的地方。 在一个非常基本的形式中,reducer可以只返回您的initialState。 将商店连接到某些组件后,我们将开始快速更改它。 一个简单的reducer文件将仅由两部分组成。
Initial State=> this is where we set the starting values for the pieces of state we are going to manage. This can be as simple as an object with one key value pair or can be as nested and complicated as your heart’s desire.
初始状态=>这是我们为要管理的状态设置起始值的位置。 它可以像具有一个键值对的对象一样简单,也可以像您的内心所希望的那样嵌套和复杂。
The Reducer Function=> it’s here where our store is going to be changed based on information we pass to this reducer. We could change a count
value and increment it by one, for example. We will make a case for many different actions as we start to grow our application. Here’s a sample reducer file that simply returns the initialState in all cases. Not very useful. Let’s work on that next.
Reducer Function =>在这里,我们将根据传递给该Reducer的信息来更改商店。 例如,我们可以更改一个count
数值并将其增加一个。 在开始扩展应用程序时,我们将说明许多不同的操作。 这是一个示例reducer文件,在所有情况下都只返回initialState。 不是很有用。 接下来让我们开始吧。
let initialState = {
count: 0
}
export const reducer = (prevState=initialState, action) => {
switch(action.type){
default:
return {...prevState}
}
}
As you can see the reducer function takes in two arguments, the prevState
or what we have in state before running the reducer, and the action
. We will get into actions more in a little bit, for now know actions are going to be called in components, sent to this reducer and something is going to happen to state based on a case in the reducer function and what we want to happen in our application.
如您所见,reducer函数接受两个参数, prevState
或运行reducer之前的状态,以及action
。 我们将稍作讨论,因为现在知道将在组件中调用动作,将其发送到此reducer,并且根据reducer函数中的情况以及我们想在其中发生的事情,状态将发生变化。我们的应用程序。
连接组件 (Hooking up a component)
Components are connected to our store via the connect
function imported from react-redux
. The connect function provides the connected component with pieces of data that it needs from the store
, and the functions it can use to dispatch actions
to the store. I think about four steps when connecting a component.
组件通过从react-redux
导入的connect
函数连接到我们的商店。 connect函数为连接的组件提供store
所需的数据,以及可用于将actions
分派给存储的功能。 连接一个组件时,我会考虑四个步骤。
1 . import { connect } from 'react-redux';
1。 import { connect } from 'react-redux';
2. Add the connect function next to the export of the component. For Example if our component was named Counter it would look like this.
2.在组件的导出旁边添加连接功能。 例如,如果我们的组件被命名为Counter,它将看起来像这样。
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
3. define the mapStateToProps
function we passed to connect
3.定义我们传递给connect
的mapStateToProps
函数
=> mapStateToProps
is where we can reach out to our state and grab the pieces that we need to use in this particular component. To stick with the counter idea we would need to know what the current count is. When we set up our state count was the key so this is how we will access it.
=> mapStateToProps
是我们可以到达状态并获取在此特定组件中需要使用的片段的地方。 为了坚持反对的想法,我们需要知道当前的计数是多少。 当我们设置状态计数时,这是关键,因此这就是我们将如何访问它的方式。
const mapStateToProps = state => {
return {
counter: state.counter
};
}
We have successfully taken a piece of state from our store and made it available to our component via props. <div> Current Counter: {props.counter} </div>
will display Current Counter: 0 when we boot our app up for the first time.
我们已经成功地从商店取得了状态,并通过道具将其提供给我们的组件。 <div> Current Counter: {props.counter} </div>
在我们首次启动应用程序时将显示“当前计数器:0”。
4 define the mapDispatchToProps
function we passed to connect
4定义我们传递给connect
的mapDispatchToProps
函数
=> mapDispatchToProps
is how we are going to send a message to our store and tell it that we need to update something inside of it. We do this by returning an object of keys where the values are dispatch
functions that take an object as an argument and must contain a key of type with a value. Here’s a quick example we can talk about.
=> mapDispatchToProps
是我们要向商店发送消息并告诉我们需要更新其中的内容的方式。 我们通过返回键的对象来实现此目的,其中键的值是dispatch
对象作为参数且必须包含带有值的键类型的dispatch
函数。 这是一个我们可以谈论的简单示例。
const mapDispatchToProps = dispatch => {
return {
incrementCounter: () => dispatch({type: 'INCREMENT'})
}
}
We’ve now created a dispatch function. We can call this function in our component, say in an onClick
listener and it will go to the reducer and search for what it is supposed to do. The above example is simple, but we can make the object much more complex if we wanted. Let’s say we want to have a button in our counter to increment the count by 100 instead of 1. We can add another dispatch in mapDispatchToProps and give it a payload to bring with it.
现在,我们创建了一个调度功能。 我们可以在组件中调用此函数,例如在onClick
侦听器中调用它,然后将其转到reducer并搜索其应执行的操作。 上面的例子很简单,但是如果需要,我们可以使对象复杂得多。 假设我们要在计数器中有一个按钮,以使计数增加100而不是1。我们可以在mapDispatchToProps中添加另一个调度,并为其提供有效负载。
const mapDispatchToProps = dispatch => {
return {
incrementCounter: () => dispatch({type: 'INCREMENT'}),
incrementByOneHundred: () => dispatch({type: 'ADD_HUNDRED, payload:{value: 100})
}
}
Here we’ve given our incrementByOneHundred
key a function where we not only pass the type
, but a payload
which is an object that has a key of value pointing to a number (100). We can now add both of these functions to onClick listeners. They won’t work yet though. We first have to handle their cases in the reducer.
在这里,我们给了我们的incrementByOneHundred
键一个函数,该函数不仅传递type
,还传递一个payload
,该payload
是一个对象,其值的键指向数字(100)。 现在,我们可以将这两个功能添加到onClick侦听器中。 他们还不会工作。 我们首先必须在减速器中处理它们的情况。
在减速器中处理发货 (Handling a Dispatch in the Reducer)
Now that we have some click listeners hooked up we are going to hit that reducer function and need to have a way to handle these cases. Here’s our reducer function from before, but we’ve added to our switch statement to handle the additional dispatches we just wrote.
现在,我们已经连接了一些单击侦听器,我们将使用该reducer函数,并且需要一种方法来处理这些情况。 这是我们之前的reducer函数,但是我们已经在switch语句中添加了它来处理刚刚编写的其他调度。
const initialState = {
counter: 0,
};
const reducer = (prevState = initialState, action) => {
switch (action.type) {
case: 'INCREMENT':
return {...prevState, counter: state.counter + 1}
case: 'ADD_HUNDRED':
return {...prevState, counter state.counter + action.payload.value}
default:
return {...prevState};
}
};
export default reducer;
We’ve added two cases to our switch statement to accommodate the new actions we built in our Counter component. When these cases hit our switch they are going to be handled accordingly. It is important that the case matches the type EXACTLY or it isn’t going to work. You’ll also notice that we’re accessing that value we sent along as well through action.payload.value
我们在switch语句中添加了两种情况,以适应我们在Counter组件中构建的新操作。 当这些案例击中我们的开关时,将对其进行相应处理。 重要的是,案例必须与类型完全匹配,否则将无法正常工作。 您还会注意到,我们正在访问通过action.payload.value
Once the component is hooked up and you are handling the cases in the reducer. You should be seeing changes to your state as you’re making them in your project.
组件连接好之后,您就可以在减速器中处理箱子了。 在项目中进行状态更改时,您应该会看到状态更改。
I shied away from Redux for a long time simply because I heard other people had trouble with it, or people said it was ‘hard’. Don’t do what I did. Try to build a simple one page app with a counter where you can increase, decrease, add forty, divide by 7 whatever you want. Get it working on one simple component like this first and then take on the challenge of converting one of your old projects to use Redux. Your brain will thank you for it. If you have trouble? Reach out to me on LinkedIn, I’d be happy to help and talk through some of the Redux basics.
我避开Redux了很长一段时间,只是因为我听说其他人对此有麻烦,或者人们说这很“困难”。 不要做我做的事。 尝试构建一个带有计数器的简单一页应用程序,您可以在其中增加,减少,增加40,除以7。 首先让它在一个简单的组件上工作,然后再面对将一个旧项目转换为使用Redux的挑战。 您的大脑将为此感谢您。 如果有麻烦? 在LinkedIn上与我联系,我很乐于帮助和讨论Redux的一些基础知识。
快乐黑客, (Happy Hacking,)
资源资源 (Resources)
其他事情: (Other Things:)
翻译自: https://medium.com/@robert.keller22/things-i-did-this-week-react-redux-110b7899f4b6