决定何时将组件连接到 Redux Store
介绍
在构建可扩展的 Web 应用程序时,Redux 是我首选的状态管理工具。无论最初要经历多么麻烦,只要代码库足够大,每个人都会感谢 Redux 拯救了一切。使用 Redux 的主要麻烦是将组件连接到 Redux 存储和操作。虽然通常最好将所有 Redux 绑定放在一个地方(通常是最顶层的组件),但这种做法很快就会因复杂的组件层次结构而变得混乱。
待办事项++
在本练习中,我们将构建臭名昭著的 ToDo 应用程序的扩展版本。虽然无聊的 ToDo 应用程序只有一种类型的项目,即:文本项目,但我们的 ToDo++ 项目可以是
- 文本项目
- 核对清单项目
- 带有动作的项目
- 以上混合
考虑到这个要求,让我们通过组件分解来启动我们的设计过程。首先,我们知道我们只有一个页面,AppComponent,我们将在其中展示我们的项目。但是,作为优秀的前端工程师,我们总是预见到以后的变化,所以我们会将我们的待办事项列表功能封装在一个新组件中,并将组件与页面隔离开来。ViewPageComponent 将是我们应用中唯一的页面。只需将它放在那里就可以避免将所有内容都放在 App.js 中造成的混乱。因此,我们的初始结构如下所示:
AppComponent
|- ViewPageComponent
|- ToDoListComponent
TodoList 由什么组成?当然是 ToDoItems。虽然目前只有四种 ToDo 项类型,但我们希望为将来的添加留出空间。因此,我们将项与列表分离。然后,我们进一步将项分解为原子组件,从上述要求中汲取灵感。
AppComponent
|- ViewPageComponent
|- ToDoListComponent
|- ToDoItemComponent
|- TextComponent
|- CheckComponent
|- ButtonComponent
注意ToDoItemComponent 与原子组件之间的关系不是继承而是聚合。这样做的原因是每个 ToDoItem 都会包含一个或多个原子组件,而不仅仅是其中一个组件。
现在组件分解已经完成,让我们深入实施。
样板
如果您不想从头开始设置 React-Redux 项目,那么您可以继续从GitHub repo克隆代码。
接下来,让我们首先使用React CLI创建一个新的 React 应用程序。在你的终端中,使用以下命令生成该应用程序:
$ create-react-app todoplus
$ cd todoplus
接下来,我们将 Redux 添加到我们的项目中:
$ npm install --save redux react-redux
完成后,我们创建页面组件并初始化 Redux 的 store、actions 和 Reducer。源的目录结构如下:
todoplus
|- src
|- ToDoListComponent
|- ToDoItemComponent.js
|- TextComponent.js
|- CheckComponent.js
|- ButtonComponent.js
|- index.js
|- actions.js
|- App.css
|- App.js
|- index.css
|- index.js
|- reducers.js
|- store.js
// actions.js
// These are our action types
export const DELETE_ITEM = "DELETE_ITEM"
export const PRINT_ITEM = "PRINT_ITEM"
export const TOGGLE_CHECK = "TOGGLE_CHECK"
// Now we define actions
export function deleteItem(itemIndex){
return {
type: DELETE_ITEM,
itemIndex
}
}
export function printItem(itemIndex){
return {
type: PRINT_ITEM,
itemIndex
}
}
export function togglCheck(itemIndex){
return {
type: TOGGLE_CHECK,
itemIndex
}
}
// reducers.js
import { DELETE_ITEM, PRINT_ITEM, TOGGLE_CHECK } from './actions';
const initialState = {
tasks: [
{
label: "Task 01",
isCheckItem: false,
hasActions: false,
isChecked: false
},
{
label: "Task 02",
isCheckItem: true,
hasActions: false,
isChecked: true
},
{
label: "Task 03",
isCheckItem: false,
hasActions: true,
isChecked: false
},
{
label: "Task 04",
isCheckItem: true,
hasActions: true,
isChecked: false
}
]
}
export default function todoReducer(state, action) {
if (typeof state === 'undefined') {
return initialState
}
switch(action.type){
case DELETE_ITEM:
console.log("deleting");
break;
case EDIT_ITEM:
console.log("editing");
break;
case TOGGLE_CHECK:
var items = [...state.tasks];
items[action.itemIndex].isChecked = !items[action.itemIndex].isChecked;
state = {...state, tasks: items};
}
// We create an empty reducer for now
return state
}
// store.js
import { createStore } from 'redux'
import todoReducer from './reducers'
export default createStore(todoReducer)
// ViewPageComponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import TodoListComponent from './TodoListComponent';
import { deleteItem, printItem, togglCheck } from './actions';
class ViewPageComponent extends Component {
render(){
return (
<TodoListComponent
itemList={this.props.items}
/>
);
}
}
function mapStateToProps(state) {
return {
items: state.tasks
}
}
const mapDispatchToProps = {
deleteItem,
printItem,
togglCheck
};
export default connect(mapStateToProps, mapDispatchToProps)(ViewPageComponent);
// App.js
import React from 'react';
import { Provider } from 'react-redux'
import store from './store';
import ViewPageComponent from './ViewPageComponent';
function App() {
return (
<Provider store={store}>
<ViewPageComponent />
</Provider>
);
}
export default App;
请注意,ViewPageComponent 已连接到 Redux 存储和操作。让我们花点时间分析一下我们在 Reducer 中使用的数据结构。
在本指南中,我们假设所有数据都已获取并存储在Reducer 中的任务状态中。此外,应用程序将仅显示任务,而不会处理将新任务插入列表。
观察Reducers.js中的任务数组,我们可以识别出我们必须从组件中渲染的四种类型的任务:
- 任务 01 - 纯文本项目
- 任务 02 - 复选框项目
- 任务 03 - 带有操作按钮的文本项
- 任务 04 - 带有操作按钮的复选框项
我们还预定义了两个动作:
- 删除项目操作 - 这将从任务列表中删除当前项目
- 打印项目操作-这将简单地将任务文本打印到控制台输出
记住这些,让我们继续我们的旅程。
构建纯文本的 Todo
应用程序的第一个版本将仅显示“文本”项。这将使我们能够构建应用程序的核心组件,然后可以对其进行扩展。我们将从 TextComponent 开始。现在,我们需要确定此组件是否需要直接访问商店。按照传统的思维过程,我们将把 TextComponent 建模为一个哑组件,并使用 props 传递显示内容所需的任何属性。
注意:
关于使用 redux store 连接的传统思维过程有两条一般建议 (1) 尽量减少 Redux 连接在应用程序中的传播,即:尽可能集中于少数组件;(2) 将连接提供给层次结构中更高的组件。这两条一般都有用,除非情况需要更多技巧。
export default function TextComponent({ itemId, itemText }){
return (
<div className="list-item-text">
<span>{itemText}</span>
</div>
)
}
完成后,我们将创建 ToDoItemComponent。同样,我们将采用商店连接处于最高级别的策略。因此,任何属性都将通过 props 传递。
import TextComponent from "./TextComponent";
export default function({ item }){
return (
<div className="list-item">
<TextComponent
itemId={item.id}
itemText={item.label}
/>
</div>
)
}
最后,我们创建 TodoListComponent(在 index.js 中)。不过,该组件不会直接与 React 存储或操作交互。必要的信息(即项目列表)将从父组件传递。此外,我们将此组件添加到 ViewPageComponent 并将项目列表传递给它,这样我们就可以验证到目前为止一切是否正常。
// TodoListComponent/index.js
import React from 'react';
import ToDoItemComponent from "./ToDoItemComponent";
export default function TodoListComponent({itemList}){
return (
<div className="todo-list">
{itemList.map((item, idx) => (
<ToDoItemComponent item={item} key={idx} />
))}
</div>
)
}
//ViewPageComponent.js
// ...
class ViewPageComponent extends Component {
render(){
return (
<TodoListComponent itemList={this.props.items} />
);
}
}
// ...
如果你运行它并测试,它应该会显示这些项目。一切都很好。
扩展 Todo:添加复选框项目
接下来,我们将添加复选框项。这应该很容易。首先,我们创建 CheckComponent,然后修改 TodoItemComponent 以反映选中项。现在出现了一个问题,我们应该把复选框切换操作放在哪里?让我们采用当前模式,并从层次结构中的最高层传递切换操作:ViewPageComponent。
// CheckComponent.js
import React from 'react';
export default function CheckComponent</
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~