And a data stream Redux Store

Store

actions in the previous chapter, we define for "what happened" and the fact that these actions Reducer update status.

Store is the object gathered them together. Store has the following responsibilities:

  • Hold application state;
  • Allows access status getState ();
  • Allow status updates through the dispatch (action);
  • By subscribing to register subscribe (listener);
  • By function subscribe (listener) returns to deal with Logout listener.

It is important to note that you will have only one store in Redux application. When you want to split your data processing logic, you will use a combination of reducer rather than many store.
If you have a reducer, it is easy to create a store. In the previous section, we used combineReducers () multiple reducer into one. We now import it and pass it to createStore ().

1
2
3
import { createStore } from 'redux'
import todoApp from './reducers'
const store = createStore(todoApp)

You can optionally specify the initial state as createStore () is the second parameter. This match is very useful to ensure that the client state status and Redux applications running on the server.

1
const store = createStore(todoApp, window.STATE_FROM_SERVER)

Dispatching Actions

Now that we have created a store, let us work to validate our program! Even without any UI, we can also test the update logic.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import {
addTodo,
toggleTodo,
setVisibilityFilter,
VisibilityFilters
} from './actions'


console.log(store.getState())

// Every time the state changes, log it
// Note that subscribe() returns a function for unregistering the listener
const unsubscribe = store.subscribe(() =>
console.log(store.getState())
)

// Dispatch some actions
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))
store.dispatch(addTodo('Learn about store'))
store.dispatch(toggleTodo(0))
store.dispatch(toggleTodo(1))
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED))

// Stop listening to state updates
unsubscribe()

You can see how this leads to a change in the state store:
display Image
Before we start writing UI, we specify the behavior of our application. We will not do it in this tutorial, but you can now write tests for your reducer and actionCreator. You do not laugh at anything because they are just purely functional. Call them and make assertions on the content of their return.

index.js

1
2
3
4
import { createStore } from 'redux'
import todoApp from './reducers'

const store = createStore(todoApp)

Data Flow

Redux architecture carried out around a strict one-way data flow.
This means that all the data in the application life cycle follows the same pattern, the logic of your application more predictable and easier to understand. It also encourages the standardization of data, so that multiple copies of the final will not be independent of each other does not know the same data.
If you are still unsure, please read Motivation and The Case for Flux, in order to obtain a compelling argument in favor of a one-way flow of data. Although Redux is not entirely Flux, but it has the same key advantages.
Data Lifecycle any Redux application are the following four steps:
1. You can call store.dispatch (action)
action is a simple object happenings description. E.g:

1
2
3
{ type: 'LIKE_ARTICLE', articleId: 42 }
{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
{ type: 'ADD_TODO', text: 'Read the Redux docs.' }

The action is seen as a very brief news segments. "Mary likes Article 42" or "read Redux documents." Todos is added to the list. "
You can call from anywhere in the application store.dispatch (action), including components and XHR callback, or even at a predetermined time interval.
2.Redux Store calls reducer function that you provide.
Store will deliver two reducer parameters: current status and operation of the tree example, todo application, the root reducer may receive the content as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// The current application state (list of todos and chosen filter)
let previousState = {
visibleTodoFilter: 'SHOW_ALL',
todos: [
{
text: 'Read the docs.',
complete: false
}
]
}

// The action being performed (adding a todo)
let action = {
type: 'ADD_TODO',
text: 'Understand the flow.'
}

// Your reducer returns the next application state
let nextState = todoApp(previousState, action)

Please note, reducer is a pure function. It only calculates the next state. It should be completely predictable: multiple calls to the same input should produce the same output. It should not perform any side effects or router API calls conversion. These should occur before action sent.
3. Root reducer reducer may output a plurality of trees into a single state.
How do you build roots reducer entirely up to you. Providing Redux combineReducers () helper function for the reducer root "exploded" separate function, a status management function for each branch of the tree.
This is combineReducers () works. Suppose you have two reducer, one for the todos list, and one for the currently selected filter settings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function (state = [], action) {
// Somehow calculate it...
return nextState
}

function visibleTodoFilter(state = 'SHOW_ALL', action) {
// Somehow calculate it...
return nextState
}

let todoApp = combineReducers({
todos,
visibleTodoFilter
})

当你发出一个动作时,由combineReducers返回的todoApp将调用两个reducer:

1
2
let nextTodos = todos(state.todos, action)
let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action)

然后它会将两组结果合并为一个状态树:

1
2
3
4
 return {
todos: nextTodos,
visibleTodoFilter: nextVisibleTodoFilter
}

虽然combineReducers()是一个方便的助手实用程序,但您不必使用它;随时写你自己的根reducer!
4.Redux存储保存由根reducer返回的完整状态树。
这棵新树现在是你的应用程序的下一个状态!现在每个用store.subscribe(listener)注册的监听器都会被调用;监听器可以调用store.getState()来获取当前状态。
现在,UI可以更新以反映新的状态。如果使用React Redux之类的绑定,则这是调用component.setState(newState)的点。

React

从一开始,我们需要强调Redux与React没有任何关系。您可以使用React,Angular,Ember,jQuery或vanilla JavaScript编写Redux应用程序。
也就是说,Redux与React和Deku等库合作得非常好,因为它们让您将UI描述为状态的函数,Redux响应操作发出状态更新。
我们将使用React来构建我们简单的待办事项应用程序。

React Redux install

React绑定不包含在默认的Redux中。你需要明确地安装它们:

1
npm install --save react-redux

如果您不使用npm,您可以从unpkg获取最新的UMD版本(开发版或生产版)。如果您通过script标记将其添加到您的页面,则UMD构建会导出一个名为window.ReactRedux的全局。

展示和容器组件

Redux的React绑定包含分离表示和容器组件的想法。如果您不熟悉这些条款,请先阅读有关条款,然后再回来。他们很重要,所以我们会等待!
完成阅读文章?我们来重述一下他们的区别:

































展示 Components 容器 Components
含义 看起来如何(标记,样式) 工作原理(数据读取,状态更新)
Redux No
Yes
读取数据 从props读取数据 订阅Redux状态
改变数据 从props调用回调 Dispatch Redux actions
写入 手动写 通常由React Redux生成

Most of the components that we will write all show, but we need to generate some container components to connect them to the Redux storage. This design does not mean SUMMARY The following components must be near the top of the container component tree. If a container assembly becomes too complex (i.e., it has a lot of nested display assembly, and there are numerous callbacks are passed on), then introduced into another container in the component tree, as the FAQ.
Technically, you can () hand-coded assembly container with store.subscribe. We do not recommend that you do so because React Redux will be difficult to complete many performance optimization. For this reason, we will use the supplied connect React Redux () function generates them, instead of writing the container assembly, as shown below.

Design component hierarchy

Remember how we design the shape of the root state of the object? It's time we designed the UI hierarchy to match it. This is not specific to the Redux tasks. In React in thinking is a good tutorial, can explain the process.
Our design brief was simple. We want to show to-do list. When you click, after the completion of crossed-do. We want to display a field where the user can add new to-do. At the bottom, we want to show all switch the display to show only completed activities or only do list.

Design presentation component

I see the following presentation components and their props appear in this brief introduction:

  • ToDoList is a list showing visible backlogs.
    • todos: Array array comprising {id, text, completed} shape todo items.
    • onTodoClick (id: number) callback is invoked when you click todo.
  • ToDo is a single-do
    • text: string is the text to be displayed.
    • completed: boolean whether to-do should appear crossed out.
    • onClick () callback is invoked when you click todo.
  • Link is a callback link.
    • onClick () callback is invoked when you click the link.
  • Footer is that we allow the user to change the currently visible to-do place.
  • App是呈现其他所有内容的根组件。
    他们描述的外观,但不知道数据来自哪里,或者如何改变它。他们只渲染给他们的东西。如果你从Redux迁移到其他的东西,你将能够保持所有这些组件完全一样。他们没有依赖Redux。

设计容器组件

我们还需要一些容器组件将演示组件连接到Redux。例如,演示TodoList组件需要一个容器,如VisibleTodoList,该容器订阅Redux存储并知道如何应用当前可见性过滤器。要更改可见性过滤器,我们将提供一个FilterLink容器组件,用于呈现链接,并在点击时分配适当的操作:

  • VisibleTodoList根据当前可见性过滤器过滤待办事项并呈现TodoList。
  • FilterLink获取当前可见性过滤器并呈现链接。
    • filter: string是它代表的可见性过滤器。

设计其他组件

有时很难判断某个组件应该是一个表示组件还是一个容器。例如,有时窗体和函数实际上是耦合在一起的,比如在这个小部件的情况下:

  • AddTodo是一个带有“添加”按钮的输入字段

从技术上讲,我们可以将它分成两个部分,但现阶段可能为时过早。在非常小的组件中混合表示和逻辑是很好的。随着它的增长,如何分割它将会更加明显,所以我们会把它混合起来。

实现组件

我们来编写组件!我们从演示组件开始,所以我们不需要考虑绑定到Redux。

实现演示组件

这些都是普通的React组件,所以我们不会详细检查它们。我们编写功能无状态的组件,除非我们需要使用本地状态或生命周期方法。这并不意味着表示组件必须是功能 - 这样更容易定义它们。如果您需要添加本地状态,生命周期方法或性能优化,则可以将它们转换为类。
components/Todo.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react'
import PropTypes from 'prop-types'

const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
style={ {
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
</li>
)

Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}

export default Todo

components/TodoList.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react'
import PropTypes from 'prop-types'
import Todo from './Todo'

const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map((todo, index) => (
<Todo key={index} {...todo} onClick={() => onTodoClick(index)} />
))}
</ul>
)

TodoList.propTypes = {
todos: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired
).isRequired,
onTodoClick: PropTypes.func.isRequired
}

export default TodoList

components/Link.js

大专栏 Redux Store以及数据流  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

importimport ReactReact fromfrom ''reactreact''
importimport PropTypesPropTypes fromfrom ''prop-typesprop-types''

constconst LinkLink == ({ active, children, onClick }) ({ active, childr => {
if (active) {
return <span>{children}</span>
}

return (
<a
href=""
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
}

Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}

export default Link

components/Footer.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

importimport ReactReact fromfrom ''reactreact''
importimport FilterLinkFilterLink fromfrom ''../containers/FilterLink../containers/Filter '
import { VisibilityFilters } from '../actions'

const Footer = () => (
<p>
Show:
{' '}
<FilterLink filter={VisibilityFilters.SHOW_ALL}>
All
</FilterLink>
{', '}
<FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>
Active
</FilterLink>
{', '}
<FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>
Completed
</FilterLink>
</p>
)

export default Footer

实现容器组件

现在是时候通过创建一些容器将这些表示性组件连接到Redux。从技术上讲,容器组件只是一个React组件,它使用store.subscribe()来读取Redux状态树的一部分,并为它呈现的呈现组件提供props。您可以手动编写容器组件,但我们建议使用React Redux库的connect()函数生成容器组件,该函数提供许多有用的优化以防止不必要的重新呈现。(这样做的一个结果是你不必担心自己实现shouldComponentUpdate的React性能建议。)
要使用connect(),需要定义一个名为mapStateToProps的特殊函数,它告诉如何将当前的Redux存储状态转换为要传递给要包装的表示组件的道具。例如,VisibleTodoList需要计算待办事项以传递给TodoList,所以我们定义一个函数,根据state.visibilityFilter过滤state.todos,并在其mapStateToProps中使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
case 'SHOW_ALL':
default:
return todos
}
}

const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}

除了读取状态之外,容器组件还可以调度操作。以类似的方式,您可以定义一个名为mapDispatchToProps()的函数,它接收dispatch()方法并返回要注入演示组件的回调支持。例如,我们希望VisibleTodoList将名为onTodoClick的道具注入TodoList组件,并且我们希望onTodoClick发送TOGGLE_TODO操作:

1
2
3
4
5
6
7
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}

最后,我们通过调用connect()并传递这两个函数来创建VisibleTodoList:

1
2
3
4
5
6
7
8
import { connect } from 'react-redux'

const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)

export default VisibleTodoList

这些是React Redux API的基础知识,但有几个捷径和开关选项,因此我们鼓励您详细查看其文档。如果您担心mapStateToProps过于频繁地创建新对象,则可能需要了解使用重新选择计算派生数据。
查找下面定义的其余容器组件:
container/FilterLink.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
}

const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter))
}
}
}

const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(Link)

export default FilterLink

containers/VisibleTodoList.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'

const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}

const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}

const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}

const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)

export default VisibleTodoList

实现其他组件

containers/AddTodo.js
如前所述,AddTodo组件的展示和逻辑都被混合到一个单一的定义中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

let AddTodo = ({ dispatch }) => {
let input

return (
<div>
<form
onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)

export default AddTodo

如果您不熟悉ref属性,请阅读本文档以熟悉推荐使用此属性。

将容器捆绑在一个组件内

components/App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)

export default App

传递Store

所有容器组件都需要访问Redux存储,以便他们可以订阅它。一种选择是将其作为props传递给每个容器组件。然而它很乏味,因为即使是通过表示组件,也只是因为它们碰巧在组件树中渲染容器,所以你必须连线存储。
我们推荐的选项是使用名为 的特殊React Redux组件来神奇地使存储可供应用程序中的所有容器组件使用,而无需明确传递它。渲染根组件时,只需要使用它一次:
index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

const store = createStore(todoApp)

render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

Guess you like

Origin www.cnblogs.com/sanxiandoupi/p/11713241.html