如何快速入门 ReactJS 开发。第二部分:最佳实践
今天,我们将继续为您带来《如何快速入门 ReactJS 开发:3 个实用工具和最佳实践》系列文章的第二部分。我们将在此分享 React-Redux 项目结构的最佳实践,帮助您更好地理解应用程序架构。
React-Redux 项目结构的最佳实践
在本节中,我们将根据我们的经验,分享一些关于如何构建 React-Redux 项目文件和代码的基本建议,以确保您的应用程序在扩展过程中保持可维护性。该指南基于对 create-react-app 配置的改进。
最初,我们决定将 React 和 Redux 分别放在不同的文件夹中。这样可以简化任何更改或添加新功能的过程。Redux 特有的代码(reducer、action、action 类型)按照“功能优先”的模式(Re-Ducks)进行拆分。以下是我们实际使用的一个项目结构示例:
src/
├── state => redux specific code (Re-Ducks)
| ├── ducks/
| | ├── duck/
| | | ├── types.js
| | | ├── actions.js
| | | ├── selectors.js
| | | ├── reducers.js
| | | ├── tests.js
| | | ├── index.js
| utilities/ => global constants and helper functions
| views/
| ├── routes/ => base router
| ├── components/ => feature-first components
| ├── pages/ => layouts, related to routes
| ├── styled/ => StyledComponents
| └── UI/ => reusable components
我们倾向于先创建 React 组件,然后再编写相应的 Redux 代码。这样可以让我们对数据需求有一个大致的了解。
/ducks 目录遵循固定的模式。我们使用 ducks 模式的修改版本来组织我们的 Redux 代码:
ducks/
├── duck/
| ├── actions.js
| ├── reducers.js
| ├── types.js
| ├── utils.js
| ├── selectors.js
| └── index.js
└── index.js
现在,让我们来讨论每个 /duck 文件夹中的文件,以了解它为什么重要以及它代表什么。
项目结构文件
types.js
文件包含我们动作类型的字符串字面量,方便您快速找到可用的动作。这些字符串以对象字面量的形式导出,您可以将其导入到 reducer 和动作创建器中,而无需硬编码。虽然维护一个单独的文件来存放动作类型并非必须,但强烈建议您这样做,以便更好地组织项目文件结构。
// types.js
export const SOME_YOUR_TYPE = "SOME_YOUR_TYPE";
actions.js
文件中定义了所有操作。实际上,有些开发者倾向于将异步操作和操作创建器分开,但我们认为这并非至关重要。
// actions.js
import types from './types.js';
// action creator
const someAction = payload => ({
type: types.SOME_YOUR_TYPE,
payload
});
实际上,我们使用 redux 中间件,例如redux-thunk或redux-promise-middleware来分发异步操作。
reducer.js
中的 reducer 用于更新状态。我们使用 `reducer.js` 为每个 action 创建一个单独的 reducer createReducer。我们使用此命令创建 reducer,而不是基本的 switch-case 模板。这样做非常有用,例如,如果您需要限定 reducer 的一部分作用域,以便在多个语句中使用同名变量case。
// reducer.js
const someReducer = createReducer(initialState)({
[types.YOUR_ACTION_TYPE]: (state, action) => {
return {
...state,
some_prop: action.payload
};
},
[types.SOME_ANOTHER_TYPE]: (state, { payload: { data } }) => ({
...state,
data,
loading: false
}),
[types.MAY_BE_YOU_WANT_RESET]: (state, action) => ({
...initialState
})
});
在 Redux 中,选择器(
selector)是一段逻辑,它从 store 中接收特定的状态。此外,选择器还可以根据给定的状态计算数据,从而使 store 只存储基本的原始数据。选择器通常用作 store 和容器组件之间绑定的一部分。
我们使用Reselect库来创建选择器。虽然这个库并非创建选择器的唯一方法或必要条件,但它在开发体验和性能方面具有诸多优势:
- 通过 createSelector 函数创建的选择器会被记忆化。这意味着该函数会记住上次调用时传入的参数。因此,如果参数相同,它不会重新计算结果。
- 选择器可以轻松地组合/串联起来。这样,每个选择器都能保持简洁,专注于一项任务。
以下是一个简单的filteredTodos选择器示例,用于演示其工作原理:
// selector.js
import { createSelector } from 'reselect';
const todoSelector = state => state.todo.todos;
const searchTermSelector = state => state.todo.searchTerm;
export const filteredTodos = createSelector(
[todoSelector, searchTermSelector],
(todos, searchTerm) => {
return todos.filter(todo => todo.title.match(new RegExp(searchTerm, 'i')));
}
);
借助这个库,我们可以使用filteredTodossearchTerm选择器来获取所有待办事项(如果状态中没有设置),否则获取一个经过筛选的列表。
此外,结合归一化数据,我们可以将所有待办事项以扁平化的形式呈现:
import { denormalize } from 'normalizer';
import { todo } from '../../schemas';
const getById = state => state.todo.byId;
const getAllIds = state => state.todo.all;
export const makeAllTodos = () =>
createSelector(
[getAllIds, getById],
(all, todos) =>
denormalize(all, [todo], { todos}),
);
index.js
在这里,我们将所有操作、选择器和 reducer 重新导出为默认导出。
// index.js
export * from './actions';
export * from './selectors';
export { default } from './reducer';
我们的鸭子折叠器终于做好了!
我们采用这种方式组织 React 应用结构,以使应用在扩展时易于维护。
结论
在 Codica,我们制定了React-Redux 项目开发的最佳实践,可以帮助您理解应用程序架构并构建结构良好的代码。我们的专家相信,这些建议将帮助您合理组织项目结构,使其易于维护和阅读。
敬请关注并查看我们的完整文章:如何快速开始 ReactJS 开发:3 个可靠的工具和最佳实践。
文章来源:https://dev.to/codicacom/how-to-start-reactjs-development-fast-part-2-2lp2