├── .gitignore ├── .prettierrc.json ├── CNAME ├── README.md ├── docs ├── FAQ.md ├── api │ ├── Store.md │ ├── api-reference.md │ ├── applyMiddleware.md │ ├── bindActionCreators.md │ ├── combineReducers.md │ ├── compose.md │ └── createStore.md ├── components │ └── DetailedExplanation.jsx ├── faq │ ├── Actions.md │ ├── CodeStructure.md │ ├── DesignDecisions.md │ ├── General.md │ ├── ImmutableData.md │ ├── Miscellaneous.md │ ├── OrganizingState.md │ ├── Performance.md │ ├── ReactRedux.md │ ├── Reducers.md │ └── StoreSetup.md ├── introduction │ ├── CoreConcepts.md │ ├── Ecosystem.md │ ├── Examples.md │ ├── GettingStarted.md │ ├── Installation.md │ ├── LearningResources.md │ └── why-rtk-is-redux-today.md ├── redux-toolkit │ └── overview.md ├── style-guide │ └── style-guide.md ├── tutorials │ ├── essentials │ │ ├── part-1-overview-concepts.md │ │ ├── part-2-app-structure.md │ │ ├── part-3-data-flow.md │ │ ├── part-4-using-data.md │ │ ├── part-5-async-logic.md │ │ ├── part-6-performance-normalization.md │ │ ├── part-7-rtk-query-basics.md │ │ └── part-8-rtk-query-advanced.md │ ├── fundamentals │ │ ├── part-1-overview.md │ │ ├── part-2-concepts-data-flow.md │ │ ├── part-3-state-actions-reducers.md │ │ ├── part-4-store.md │ │ ├── part-5-ui-and-react.md │ │ ├── part-6-async-logic.md │ │ ├── part-7-standard-patterns.md │ │ └── part-8-modern-redux.md │ ├── quick-start.md │ ├── tutorials-index.md │ ├── typescript.md │ └── videos.md ├── understanding │ ├── history-and-design │ │ ├── PriorArt.md │ │ └── middleware.md │ └── thinking-in-redux │ │ ├── Glossary.md │ │ ├── Motivation.md │ │ └── ThreePrinciples.md └── usage │ ├── CodeSplitting.md │ ├── ConfiguringYourStore.md │ ├── ImplementingUndoHistory.md │ ├── IsolatingSubapps.md │ ├── MigratingToRedux.md │ ├── ReducingBoilerplate.md │ ├── ServerRendering.md │ ├── Troubleshooting.md │ ├── UsageWithTypescript.md │ ├── UsingObjectSpreadOperator.md │ ├── WritingTests.mdx │ ├── deriving-data-selectors.md │ ├── index.md │ ├── structuring-reducers │ ├── BasicReducerStructure.md │ ├── BeyondCombineReducers.md │ ├── ImmutableUpdatePatterns.md │ ├── InitializingState.md │ ├── NormalizingStateShape.md │ ├── PrerequisiteConcepts.md │ ├── RefactoringReducersExample.md │ ├── ReusingReducerLogic.md │ ├── SplittingReducerLogic.md │ ├── StructuringReducers.md │ ├── UpdatingNormalizedData.md │ └── UsingCombineReducers.md │ └── writing-logic-thunks.mdx ├── errors.json ├── package.json └── website ├── .gitignore ├── README.md ├── _redirects ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src ├── css │ └── custom.css ├── js │ └── monokaiTheme.js └── pages │ ├── errors.js │ ├── index.js │ └── styles.module.css ├── static ├── CNAME └── img │ ├── cogs-solid.svg │ ├── cubes-solid.svg │ ├── external-link-square-alt-solid.svg │ ├── favicon │ └── favicon.ico │ ├── github-brands.svg │ ├── noun_Check_1870817.svg │ ├── noun_debugging_1978252.svg │ ├── redux-logo-landscape.png │ ├── redux.svg │ ├── redux_white.svg │ └── tutorials │ ├── essentials │ ├── ReduxAsyncDataFlowDiagram.gif │ ├── ReduxDataFlowDiagram.gif │ ├── api-slice-contents.png │ ├── devtools-action-stacktrace.png │ ├── devtools-cached-invalidation-refetching.png │ ├── devtools-cached-requests.png │ ├── devtools-done-clicking.png │ ├── devtools-first-action.png │ ├── devtools-initial.png │ ├── devtools-posts-fulfilled.png │ ├── devtools-posts-pending.png │ ├── devtools-rtkq-cache.png │ ├── disabled-posts-fetching.png │ ├── example-initial-posts-list.png │ ├── example-initial-posts.png │ ├── example-postAdded-action.png │ ├── notifications-initial.png │ ├── notifications-new.png │ ├── one-way-data-flow.png │ ├── posts-unknownAuthor.png │ ├── postslist-optimized.png │ ├── postslist-rerender.png │ ├── userpage-rerender.png │ └── working_post_list.png │ └── fundamentals │ ├── devtools-action-tab.png │ ├── devtools-async-todoAdded-action.png │ ├── devtools-async-todoAdded-diff.png │ ├── devtools-diff-tab.png │ ├── devtools-state-tab.png │ ├── devtools-todosLoaded-action.png │ ├── immutable-error.png │ ├── initial-state-updates.png │ ├── meaningOfLife-enhancer-logging.png │ ├── print-middleware-logging.png │ ├── sayhi-enhancer-logging.png │ ├── todos-app-headerLoading.png │ ├── todos-app-markedCompleted.png │ ├── todos-app-screenshot.png │ ├── todos-app-selectorFilters.png │ ├── todos-app-showCompleted.png │ └── todos-app-todosLoaded.png └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs-back 3 | 4 | coverage 5 | 6 | dist 7 | lib 8 | es 9 | types 10 | 11 | website/translated_docs 12 | website/build/ 13 | website/node_modules 14 | website/i18n/* 15 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "none", 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | cn.redux.js.org 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Redux 中文文档](http://github.com/camsong/redux-in-chinese) [![Join the chat at https://gitter.im/camsong/redux-in-chinese](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/camsong/redux-in-chinese?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/camsong/redux-in-chinese.svg?branch=master)](https://travis-ci.org/camsong/redux-in-chinese) 2 | 3 | 4 | 5 | ➡️在线文档地址:https://cn.redux.js.org/ 6 | 7 | Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 8 | (如果你需要一个 WordPress 框架,请查看 [Redux Framework](https://reduxframework.com/)。) 9 | 10 | 可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 11 | 超爽的开发体验,比如有一个[时间旅行调试器可以编辑后实时预览](https://github.com/reduxjs/redux-devtools)。 12 | 13 | Redux 除了和 [React](https://facebook.github.io/react/) 一起用外,还支持其它界面库。 14 | 它体小精悍(只有 2kB,包括依赖)。 15 | 16 | ## 安装 17 | Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。 它围绕着 Redux core,包含我们认为构建 Redux 应用程序必不可少的包和函数。Redux Toolkit 基于我们建议的最佳实践来开发,简化了大多数 Redux 任务,防止了常见错误,并使编写 Redux 应用程序变得更加容易。 18 | 19 | ``` 20 | npm install @reduxjs/toolkit react-redux 21 | ``` 22 | 23 | 只安装 Redux core 24 | 25 | ``` 26 | npm install redux 27 | ``` 28 | 29 | 更多信息,访问 [安装文档](https://cn.redux.js.org/introduction/installation/) 30 | 31 | ## 文档 32 | 33 | - [入门](https://cn.redux.js.org/introduction/getting-started) 34 | - [教程](https://cn.redux.js.org/tutorials/index) 35 | - [使用引导](https://cn.redux.js.org/usage/) 36 | - [常见问题](https://cn.redux.js.org/faq) 37 | - [API 文档](https://cn.redux.js.org/api/api-reference) 38 | 39 | 40 | ## 借鉴 41 | 42 | Redux 改进了 [Flux](https://facebook.github.io/flux/) 的思想,但通过从 [Elm](https://github.com/evancz/elm-architecture-tutorial/) 中汲取灵感来避免其复杂性。即使您没有使用过 Flux 或 Elm,Redux 也只需几分钟即可上手。 43 | 44 | ## Logo 45 | 46 | 官方 Logo [在 Github 上可以找到](https://github.com/reduxjs/redux/tree/master/logo)。 47 | 48 | ## 重要里程碑 49 | 50 | ### 4.x 里程碑: 51 | 52 | 2022.10.1 全面完成 4.x 官方文档的翻译和校验,特别感谢以下贡献者的辛勤付出: 53 | 54 | * [HuiFei](https://github.com/guanwanxiao) 55 | * [Gonson International](https://github.com/GonsonInter) 56 | * [雨季sky](https://github.com/yujidxl) 57 | * [Flora](https://github.com/Flora-wyy) 58 | * [hellocathi](https://github.com/hellocathi) 59 | * [Healer](https://github.com/zxb-hub) 60 | * [izeal77](https://github.com/izeal77) 61 | 62 | ## 贡献者 63 | 64 | 在线链接:https://github.com/nefe/redux-in-chinese/graphs/contributors 65 | 66 | 所有贡献者列表 67 | 68 | 69 | **本文档翻译流程符合 [ETC 翻译规范](https://github.com/react-guide/ETC),欢迎你来一起完善** 70 | 71 | ## License 72 | 73 | MIT -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: faq 3 | title: FAQ 目录 4 | description: 'FAQ 目录: Redux 常见问答' 5 | --- 6 | 7 | # Redux 常见问答 8 | 9 | ## 目录 10 | 11 | - **常见问题** 12 | - [什么时候需要学习 Redux?](faq/General.md#when-should-i-learn-redux) 13 | - [什么时候需要使用 Redux?](faq/General.md#when-should-i-use-redux) 14 | - [Redux 只能与 React 一起使用吗?](faq/General.md#can-redux-only-be-used-with-react) 15 | - [是否需要特定的构建工具才能使用 Redux?](faq/General.md#do-i-need-to-have-a-particular-build-tool-to-use-redux) 16 | - **Reducers** 17 | - [如何在两个 reducers 之间共享 state?必须使用 combineReducers 吗?](faq/Reducers.md#how-do-i-share-state-between-two-reducers-do-i-have-to-use-combinereducers) 18 | - [是否必须使用 switch 语句来处理 actions 吗?](faq/Reducers.md#do-i-have-to-use-the-switch-statement-to-handle-actions) 19 | - **组织 State** 20 | - [必须将所有的 state 都放入 Redux 吗?应该使用 React 的 setState() 吗?](faq/OrganizingState.md#do-i-have-to-put-all-my-state-into-redux-should-i-ever-use-reacts-setstate) 21 | - [可以将函数、promises 或其他不可序列化的项放入 store state 吗?](faq/OrganizingState.md#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state) 22 | - [如何组织 state 中的嵌套或重复数据?](faq/OrganizingState.md#how-do-i-organize-nested-or-duplicate-data-in-my-state) 23 | - [应该将表单 state 或其他 UI state 放入 store 吗?](faq/OrganizingState.md#should-i-put-form-state-or-other-ui-state-in-my-store) 24 | - **Store 设置** 25 | - [可以或者应该创建多个 stores 吗?可以直接导入 stores,并在组件中使用它吗?](faq/StoreSetup.md#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself) 26 | - [Store enhancer 中可以有多个 middleware 链吗?Middleware 函数中的 next 和 dispatch 有什么区别?](faq/StoreSetup.md#is-it-ok-to-have-more-than-one-middleware-chain-in-my-store-enhancer-what-is-the-difference-between-next-and-dispatch-in-a-middleware-function) 27 | - [如何能够只订阅 state 的一部分?可以将 dispatch action 作为订阅的一部分吗?](faq/StoreSetup.md#how-do-i-subscribe-to-only-a-portion-of-the-state-can-i-get-the-dispatched-action-as-part-of-the-subscription) 28 | - **Actions** 29 | - [为什么 type 应该是一个字符串,或者至少是可序列化的?为什么 action types 应该是常量?](faq/Actions.md#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants) 30 | - [Reducers 和 actions 之间总是存在一对一的映射关系吗?](faq/Actions.md#is-there-always-a-one-to-one-mapping-between-reducers-and-actions) 31 | - [如何表示 AJAX 调用等“副作用”?为什么我们需要 action creator、thunk 和 middleware 这些来处理异步行为?](faq/Actions.md#how-can-i-represent-side-effects-such-as-ajax-calls-why-do-we-need-things-like-action-creators-thunks-and-middleware-to-do-async-behavior) 32 | - [应该使用哪个异步 middleware?如何在 thunk、sagas、observables 或其他中做出选择?](faq/Actions.md#what-async-middleware-should-i-use-how-do-you-decide-between-thunks-sagas-observables-or-something-else) 33 | - [应该从一个 action creator 连续 dispatch 多个 actions 吗?](faq/Actions.md#should-i-dispatch-multiple-actions-in-a-row-from-one-action-creator) 34 | - **不可变数据** 35 | - [不可变数据有什么好处?](faq/ImmutableData.md#what-are-the-benefits-of-immutability) 36 | - [为什么 Redux 需要不可变数据?](faq/ImmutableData.md#why-is-immutability-required-by-redux) 37 | - [有哪些方法可以实现数据不可变?必须使用 Immer 吗?](faq/ImmutableData.md#what-approaches-are-there-for-handling-data-immutability-do-i-have-to-use-immer) 38 | - [使用 JavaScript 进行不可变操作会有什么问题?](faq/ImmutableData.md#what-are-the-issues-with-using-plain-javascript-for-immutable-operations) 39 | - **代码结构** 40 | - [文件结构应该是什么样的?应该如何对 action creators 和 reducers 进行分组?Selectors 应该放在哪里?](faq/CodeStructure.md#what-should-my-file-structure-look-like-how-should-i-group-my-action-creators-and-reducers-in-my-project-where-should-my-selectors-go) 41 | - [应该如何在 reducers 和 action creators 之间划分逻辑?“业务逻辑”应该放在哪里?](faq/CodeStructure.md#how-should-i-split-my-logic-between-reducers-and-action-creators-where-should-my-business-logic-go) 42 | - [为什么应该使用 action creators?](faq/CodeStructure.md#why-should-i-use-action-creators) 43 | - [Websockets 以及其他持久连接应该放在哪里?](faq/CodeStructure.md#where-should-websockets-and-other-persistent-connections-live) 44 | - [如何在非组件文件中使用 Redux store?](faq/CodeStructure.md#how-can-i-use-the-redux-store-in-non-component-files) 45 | - **性能** 46 | - [Redux 在性能和架构方面的“扩展性”如何?](faq/Performance.md#how-well-does-redux-scale-in-terms-of-performance-and-architecture) 47 | - [为每个 action 调用“所有的 reducers”会不会很慢?](faq/Performance.md#wont-calling-all-my-reducers-for-each-action-be-slow) 48 | - [必须在 reducer 中深拷贝 state 吗?拷贝 state 不会很慢吗?](faq/Performance.md#do-i-have-to-deep-clone-my-state-in-a-reducer-isnt-copying-my-state-going-to-be-slow) 49 | - [如何减少 store 更新事件的数量?](faq/Performance.md#how-can-i-reduce-the-number-of-store-update-events) 50 | - [“单状态树”会导致内存问题吗?Dispatch 很多 actions 会占用内存吗?](faq/Performance.md#will-having-one-state-tree-cause-memory-problems-will-dispatching-many-actions-take-up-memory) 51 | - [缓存远端数据会导致内存问题吗?](faq/Performance.md#will-caching-remote-data-cause-memory-problems) 52 | - **设计决策** 53 | - [为什么 Redux 不将 state 和 action 传递给订阅者?](faq/DesignDecisions.md#why-doesnt-redux-pass-the-state-and-action-to-subscribers) 54 | - [为什么 Redux 不支持使用类 class 做 action 和 reducer ?](faq/DesignDecisions.md#why-doesnt-redux-support-using-classes-for-actions-and-reducers) 55 | - [为什么 middleware 签名使用柯里化?](faq/DesignDecisions.md#why-does-the-middleware-signature-use-currying) 56 | - [为什么 applyMiddleware 使用闭包进行 dispatch?](faq/DesignDecisions.md#why-does-applymiddleware-use-a-closure-for-dispatch) 57 | - [为什么 `combineReducers` 在调用每个 reducer 时不接收整个 state 作为第三个参数?](faq/DesignDecisions.md#why-doesnt-combinereducers-include-a-third-argument-with-the-entire-state-when-it-calls-each-reducer) 58 | - [为什么 mapDispatchToProps 不允许使用来自 `getState()` 或 `mapStateToProps()` 的返回值?](faq/DesignDecisions.md#why-doesnt-mapdispatchtoprops-allow-use-of-return-values-from-getstate-or-mapstatetoprops) 59 | - **React Redux** 60 | - [为什么应该使用 React-Redux?](faq/ReactRedux.md#why-should-i-use-react-redux) 61 | - [为什么我的组件没有重新渲染,或者 mapStateToProps 没有运行?](faq/ReactRedux.md#why-isnt-my-component-re-rendering-or-my-mapstatetoprops-running) 62 | - [为什么我的组件经常重新渲染?](faq/ReactRedux.md#why-is-my-component-re-rendering-too-often) 63 | - [如何加速 mapStateToProps?](faq/ReactRedux.md#how-can-i-speed-up-my-mapstatetoprops) 64 | - [为什么 connect 过的组件中没有 this.props.dispatch 可用?](faq/ReactRedux.md#why-dont-i-have-this-props-dispatch-available-in-my-connected-component) 65 | - [应该只 connect 顶层组件,还是可以 connect 树中的多个组件?](faq/ReactRedux.md#should-i-only-connect-my-top-component-or-can-i-connect-multiple-components-in-my-tree) 66 | - **其他** 67 | - [有没有更大的、“真正生产环境的” Redux 项目?](faq/Miscellaneous.md#are-there-any-larger-real-redux-projects) 68 | - [如何在 Redux 中实现身份验证?](faq/Miscellaneous.md#how-can-i-implement-authentication-in-redux) 69 | -------------------------------------------------------------------------------- /docs/api/Store.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: store 3 | title: Store 4 | description: 'API > Store: the core Redux store methods' 5 | --- 6 | 7 | # Store 8 | 9 | Store 就是用来维持应用所有的 [state 树](../understanding/thinking-in-redux/Glossary.md#state) 的一个对象。 10 | 改变 store 内 state 的惟一途径是对它 dispatch 一个 [action](../understanding/thinking-in-redux/Glossary.md#action)。 11 | 12 | Store 不是类。它只是有几个方法的对象。 13 | 要创建它,只需要把根部的 [reducing 函数](../understanding/thinking-in-redux/Glossary.md#reducer) 传递给 [`createStore`](createStore.md)。 14 | 15 | > ##### Flux 用户使用注意 16 | > 17 | > 如果你以前使用 Flux,那么你只需要注意一个重要的区别。Redux 没有 Dispatcher 且不支持多个 store。**相反,只有一个单一的 store 和一个根级的 reduce 函数[reducing function](../understanding/thinking-in-redux/Glossary.md#reducer)**。随着应用不断变大,你应该把根级的 reducer 拆成多个小的 reducers,分别独立地操作 state 树的不同部分,而不是添加新的 stores。然后你可以使用 [`combineReducers`](combineReducers.md) 来连接他们。这就像一个 React 应用只有一个根级的组件,这个根组件又由很多小组件构成。 18 | 19 | ### Store 方法 20 | 21 | - [`getState()`](#getstate) 22 | - [`dispatch(action)`](#dispatchaction) 23 | - [`subscribe(listener)`](#subscribelistener) 24 | - [`replaceReducer(nextReducer)`](#replacereducernextreducer) 25 | 26 | ## Store 方法 27 | 28 | ### getState() 29 | 30 | 返回应用当前的 state 树。 31 | 它与 store 的最后一个 reducer 返回值相同。 32 | 33 | #### 返回值 34 | 35 | _(any)_: 应用当前的 state 树。 36 | 37 | --- 38 | 39 |   40 | 41 | ### dispatch(action) 42 | 43 | dispatch action。这是触发 state 变化的惟一途径。 44 | 45 | **** 46 | 将使用当前 [`getState()`](#getstate) 的结果和传入的 `action` 以同步方式的调用 store 的 reducer 函数。它的返回值会被作为下一个 state。从现在开始,这就成为了 [`getState()`](#getstate) 的返回值,同时变化监听器(change listener)会被触发。 47 | 48 | > ##### Flux 用户使用注意 49 | > 50 | > 当你在 [reducer](../understanding/thinking-in-redux/Glossary.md#reducer) 内部调用 `dispatch` 时,将会抛出错误提示“Reducers may not dispatch actions.(Reducer 内不能 dispatch action)”。这就相当于 Flux 里的 “Cannot dispatch in a middle of dispatch(dispatch 过程中不能再 dispatch)”,但并不会引起对应的错误。在 Flux 里,当 Store 处理 action 和触发 update 事件时,dispatch 是禁止的。这个限制并不好,因为他限制了不能在生命周期回调里 dispatch action,还有其它一些本来很正常的地方。 51 | > 52 | > 在 Redux 里,只会在根 reducer 返回新 state 结束后再会调用事件监听器,因此,你可以在事件监听器里再做 dispatch。惟一使你不能在 reducer 中途 dispatch 的原因是要确保 reducer 没有副作用。如果 action 处理会产生副作用,正确的做法是使用异步 [action 创建函数](../understanding/thinking-in-redux/Glossary.md#action-creator)。 53 | 54 | #### 参数 55 | 56 | 1. `action` (_Object_): 描述应用变化的普通对象。Action 是把数据传入 store 的惟一途径,所以任何数据,无论来自 UI 事件,网络回调或者是其它资源如 WebSockets,最终都应该以 action 的形式被 dispatch。按照约定,action 具有 `type` 字段来表示它的类型。type 也可被定义为常量或者是从其它模块引入。最好使用字符串,而不是 [Symbols](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol) 作为 action,因为字符串是可以被序列化的。除了 `type` 字段外,action 对象的结构完全取决于你。参照 [Flux 标准 Action](https://github.com/acdlite/flux-standard-action) 获取如何组织 action 的建议。 57 | 58 | #### Returns 59 | 60 | (Object): 要 dispatch 的 action。 61 | 62 | #### Notes 63 | 64 | 使用 [`createStore`](createStore.md) 创建的 “纯正” store 只支持普通对象类型的 action,而且会立即传到 reducer 来执行。 65 | 66 | 但是,如果你用 [`applyMiddleware`](applyMiddleware.md) 来套住 [`createStore`](createStore.md) 时,middleware 可以修改 action 的执行,并支持执行 dispatch [异步 actions](../understanding/thinking-in-redux/Glossary.md#async-action)。异步 action 通常使用异步原语如 Promise、Observable 或者 Thunk。 67 | 68 | Middleware 是由社区创建,并不会同 Redux 一起发行。你需要手动安装 [redux-thunk](https://github.com/gaearon/redux-thunk) 或者 [redux-promise](https://github.com/acdlite/redux-promise) 库。你也可以创建自己的 middleware。 69 | 70 | 想学习如何描述异步 API 调用?看一下 action 创建函数里当前的 state,执行一个有副作用的操作,或者以链式操作执行它们,参照 [`applyMiddleware`](applyMiddleware.md) 中的示例。 71 | 72 | #### Example 73 | 74 | ```js 75 | import { createStore } from 'redux' 76 | const store = createStore(todos, ['Use Redux']) 77 | 78 | function addTodo(text) { 79 | return { 80 | type: 'ADD_TODO', 81 | text 82 | } 83 | } 84 | 85 | store.dispatch(addTodo('Read the docs')) 86 | store.dispatch(addTodo('Read about the middleware')) 87 | ``` 88 | 89 | --- 90 | 91 | ### subscribe(listener) 92 | 93 | 添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。你可以在回调函数里调用 [`getState()`](#getstate) 来拿到当前 state。 94 | 95 | 你可以在变化监听器里面进行 [`dispatch()`](#dispatchaction),但你需要注意下面的事项: 96 | 97 | 1. 监听器调用 [`dispatch()`](#dispatchaction) 仅仅应当发生在响应用户的 actions 或者特殊的条件限制下(比如: 在 store 有一个特殊的字段时 dispatch action)。虽然没有任何条件去调用 [`dispatch()`](#dispatchaction) 在技术上是可行的,但是随着每次 [`dispatch()`](#dispatchaction) 改变 store 可能会导致陷入无穷的循环。 98 | 99 | 2. 订阅器(subscriptions) 在每次 [`dispatch()`](#dispatchaction) 调用之前都会保存一份快照。当你在正在调用监听器(listener)的时候订阅(subscribe)或者去掉订阅(unsubscribe),对当前的 [`dispatch()`](#dispatchaction) 不会有任何影响。但是对于下一次的 [`dispatch()`](#dispatchaction),无论嵌套与否,都会使用订阅列表里最近的一次快照。 100 | 101 | 3. 订阅器不应该注意到所有 state 的变化,在订阅器被调用之前,往往由于嵌套的 [`dispatch()`](#dispatchaction) 导致 state 发生多次的改变。保证所有的监听器都注册在 [`dispatch()`](#dispatchaction) 启动之前,这样,在调用监听器的时候就会传入监听器所存在时间里最新的一次 state。 102 | 103 | 这是一个底层 API。多数情况下,你不会直接使用它,会使用一些 React(或其它库)的绑定。如果你想让回调函数执行的时候使用当前的 state,你可以 [写一个定制的 `observeStore` 工具](https://github.com/rackt/redux/issues/303#issuecomment-125184409)。 `Store` 也是一个 [`Observable`](https://github.com/zenparsing/es-observable), 所以你可以使用 [RxJS](https://github.com/ReactiveX/RxJS) 的这样的库来 `subscribe` 订阅更新。 104 | 105 | 如果需要解绑这个变化监听器,执行 `subscribe` 返回的函数即可。 106 | 107 | #### 参数 108 | 109 | 1. `listener` (_Function_): 每当 dispatch action 的时候都会执行的回调。state 树中的一部分可能已经变化。你可以在回调函数里调用 [`getState()`](#getstate) 来拿到当前 state。store 的 reducer 应该是纯函数,因此你可能需要对 state 树中的引用做深度比较来确定它的值是否有变化。 110 | 111 | ##### 返回值 112 | 113 | (_Function_): 一个可以解绑变化监听器的函数。 114 | 115 | ##### 示例 116 | 117 | ```js 118 | function select(state) { 119 | return state.some.deep.property 120 | } 121 | 122 | let currentValue 123 | function handleChange() { 124 | let previousValue = currentValue 125 | currentValue = select(store.getState()) 126 | 127 | if (previousValue !== currentValue) { 128 | console.log( 129 | 'Some deep nested property changed from', 130 | previousValue, 131 | 'to', 132 | currentValue 133 | ) 134 | } 135 | } 136 | 137 | const unsubscribe = store.subscribe(handleChange) 138 | unsubscribe() 139 | ``` 140 | 141 | --- 142 | 143 | ### replaceReducer(nextReducer) 144 | 145 | 替换 store 当前用来计算 state 的 reducer。 146 | 147 | 这是一个高级 API。只有在你需要实现代码分隔,而且需要立即加载一些 reducer 的时候才可能会用到它。在实现 Redux 热加载机制的时候也可能会用到。 148 | 149 | #### 参数 150 | 151 | 1. `nextReducer` (_Function_) store 会使用的下一个 reducer。 152 | -------------------------------------------------------------------------------- /docs/api/api-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-reference 3 | title: API 参考 4 | hide_title: false 5 | --- 6 | 7 | # API 参考 8 | 9 | Redux 的 API 非常少。Redux 为你定义了一系列的约定(例如 [reducers](../understanding/thinking-in-redux/Glossary.md#reducer)),同时提供了少量的辅助函数来把这些约定整合到一起。 10 | 11 | 这一章会介绍所有的 Redux API。记住,Redux 只关心如何管理 state。在实际的项目中,你还需要使用如 [react-redux](https://github.com/gaearon/react-redux)这样的 UI 绑定库。 12 | 13 | ### 顶级暴露的方法 14 | 15 | - [createStore(reducer, [preloadedState], [enhancer])](createStore.md) 16 | - [combineReducers(reducers)](combineReducers.md) 17 | - [applyMiddleware(...middlewares)](applyMiddleware.md) 18 | - [bindActionCreators(actionCreators, dispatch)](bindActionCreators.md) 19 | - [compose(...functions)](compose.md) 20 | 21 | ### Store API 22 | 23 | - [Store](Store.md) 24 | - [getState()](Store.md#getState) 25 | - [dispatch(action)](Store.md#dispatchaction) 26 | - [subscribe(listener)](Store.md#subscribelistener) 27 | - [replaceReducer(nextReducer)](Store.md#replacereducernextreducer) 28 | 29 | ### 引入 30 | 31 | 上面介绍的所有函数都是顶级暴露的方法。都可以这样引入: 32 | 33 | #### ES6 34 | 35 | ```js 36 | import { createStore } from 'redux' 37 | ``` 38 | 39 | #### ES5 (CommonJS) 40 | 41 | ```js 42 | var createStore = require('redux').createStore 43 | ``` 44 | 45 | #### ES5 (UMD build) 46 | 47 | ```js 48 | var createStore = Redux.createStore 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/api/applyMiddleware.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: applymiddleware 3 | title: applyMiddleware 4 | hide_title: true 5 | description: 'API > applyMiddleware: extending the Redux store' 6 | --- 7 | 8 |   9 | 10 | # `applyMiddleware(...middleware)` 11 | 12 | 使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。Middleware 可以让你包装 store 的 [`dispatch`](Store.md#dispatchaction) 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。 13 | 14 | Middleware 最常见的使用场景是无需引用大量代码或依赖类似 [Rx](https://github.com/Reactive-Extensions/RxJS) 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch [异步 actions](../understanding/thinking-in-redux/Glossary.md#async-action)。 15 | 16 | 例如,[redux-thunk](https://github.com/gaearon/redux-thunk) 支持 dispatch function,以此让 action creator 控制反转。被 dispatch 的 function 会接收 [`dispatch`](Store.md#dispatchaction) 作为参数,并且可以异步调用它。这类的 function 就称为 _thunk_。另一个 middleware 的示例是 [redux-promise](https://github.com/acdlite/redux-promise)。它支持 dispatch 一个异步的 [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) action,并且在 Promise resolve 后可以 dispatch 一个普通的 action。 17 | 18 | Middleware 并不需要和 [`createStore`](createStore.md) 绑在一起使用,也不是 Redux 架构的基础组成部分,但它带来的益处让我们认为有必要在 Redux 核心中包含对它的支持。因此,虽然不同的 middleware 可能在易用性和用法上有所不同,它仍被作为扩展 [`dispatch`](Store.md#dispatchaction) 的唯一标准的方式。 19 | 20 | #### 参数 21 | 22 | - `...middleware` (_arguments_): 遵循 Redux _middleware API_ 的函数。每个 middleware 接受 [`Store`](Store.md) 的 [`dispatch`](Store.md#dispatchaction) 和 [`getState`](Store.md#getState) 函数作为命名参数,并返回一个函数。该函数会被传入被称为 `next` 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 `next(action)`,或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 [`dispatch`](Store.md#dispatchaction) 方法作为 `next` 参数,并借此结束调用链。所以,middleware 的函数签名是 `({ getState, dispatch }) => next => action`。 23 | 24 | #### 返回值 25 | 26 | (_Function_) 一个应用了 middleware 后的 store enhancer。这个 store enhancer 的签名是 `createStore => createStore`,但是最简单的使用方法就是直接作为最后一个 `enhancer` 参数传递给 [`createStore()`](createStore.md) 函数。 27 | 28 | #### 示例: 自定义 Logger Middleware 29 | 30 | ```js 31 | import { createStore, applyMiddleware } from 'redux' 32 | import todos from './reducers' 33 | 34 | function logger({ getState }) { 35 | return next => action => { 36 | console.log('will dispatch', action) 37 | 38 | // 调用 middleware 链中下一个 middleware 的 dispatch。 39 | const returnValue = next(action) 40 | 41 | console.log('state after dispatch', getState()) 42 | 43 | // 一般会是 action 本身,除非 44 | // 后面的 middleware 修改了它。 45 | return returnValue 46 | } 47 | } 48 | 49 | const store = createStore(todos, ['Use Redux'], applyMiddleware(logger)) 50 | 51 | store.dispatch({ 52 | type: 'ADD_TODO', 53 | text: 'Understand the middleware' 54 | }) 55 | // (middleware 将打印如下信息:) 56 | // will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' } 57 | // state after dispatch: [ 'Use Redux', 'Understand the middleware' ] 58 | ``` 59 | 60 | #### 示例: 使用 Thunk Middleware 来做异步 Action 61 | 62 | ```js 63 | import { createStore, combineReducers, applyMiddleware } from 'redux' 64 | import thunk from 'redux-thunk' 65 | import * as reducers from './reducers' 66 | 67 | const reducer = combineReducers(reducers) 68 | // applyMiddleware 为 createStore 注入了 middleware: 69 | const store = createStore(reducer, applyMiddleware(thunk)) 70 | 71 | function fetchSecretSauce() { 72 | return fetch('https://www.google.com/search?q=secret+sauce') 73 | } 74 | 75 | // 这些是你已熟悉的普通 action creator。 76 | // 它们返回的 action 不需要任何 middleware 就能被 dispatch。 77 | // 但是,他们只表达「事实」,并不表达「异步数据流」 78 | function makeASandwich(forPerson, secretSauce) { 79 | return { 80 | type: 'MAKE_SANDWICH', 81 | forPerson, 82 | secretSauce 83 | } 84 | } 85 | 86 | function apologize(fromPerson, toPerson, error) { 87 | return { 88 | type: 'APOLOGIZE', 89 | fromPerson, 90 | toPerson, 91 | error 92 | } 93 | } 94 | 95 | function withdrawMoney(amount) { 96 | return { 97 | type: 'WITHDRAW', 98 | amount 99 | } 100 | } 101 | 102 | // 即使不使用 middleware,你也可以 dispatch action: 103 | store.dispatch(withdrawMoney(100)) 104 | 105 | // 但是怎样处理异步 action 呢, 106 | // 比如 API 调用,或者是路由跳转? 107 | 108 | // 来看一下 thunk。 109 | // Thunk 就是一个返回函数的函数。 110 | // 下面就是一个 thunk。 111 | function makeASandwichWithSecretSauce(forPerson) { 112 | // 控制反转! 113 | // 返回一个接收 `dispatch` 的函数。 114 | // Thunk middleware 知道如何把异步的 thunk action 转为普通 action。 115 | return function(dispatch) { 116 | return fetchSecretSauce().then( 117 | sauce => dispatch(makeASandwich(forPerson, sauce)), 118 | error => dispatch(apologize('The Sandwich Shop', forPerson, error)) 119 | ) 120 | } 121 | } 122 | 123 | // Thunk middleware 可以让我们像 dispatch 普通 action 124 | // 一样 dispatch 异步的 thunk action。 125 | store.dispatch(makeASandwichWithSecretSauce('Me')) 126 | 127 | // 它甚至负责回传 thunk 被 dispatch 后返回的值, 128 | // 所以可以继续串连 Promise,调用它的 .then() 方法。 129 | store.dispatch(makeASandwichWithSecretSauce('My wife')).then(() => { 130 | console.log('Done!') 131 | }) 132 | 133 | // 实际上,可以写一个 dispatch 其它 action creator 里 134 | // 普通 action 和异步 action 的 action creator, 135 | // 而且可以使用 Promise 来控制数据流。 136 | function makeSandwichesForEverybody() { 137 | return function(dispatch, getState) { 138 | if (!getState().sandwiches.isShopOpen) { 139 | // 返回 Promise 并不是必须的,但这是一个很好的约定, 140 | // 为了让调用者能够在异步的 dispatch 结果上直接调用 .then() 方法。 141 | return Promise.resolve() 142 | } 143 | 144 | // 可以 dispatch 普通 action 对象和其它 thunk, 145 | // 这样我们就可以在一个数据流中组合多个异步 action。 146 | return dispatch(makeASandwichWithSecretSauce('My Grandma')) 147 | .then(() => 148 | Promise.all([ 149 | dispatch(makeASandwichWithSecretSauce('Me')), 150 | dispatch(makeASandwichWithSecretSauce('My wife')) 151 | ]) 152 | ) 153 | .then(() => dispatch(makeASandwichWithSecretSauce('Our kids'))) 154 | .then(() => 155 | dispatch( 156 | getState().myMoney > 42 157 | ? withdrawMoney(42) 158 | : apologize('Me', 'The Sandwich Shop') 159 | ) 160 | ) 161 | } 162 | } 163 | 164 | // 这在服务端渲染时很有用,因为我可以等到数据 165 | // 准备好后,同步的渲染应用。 166 | 167 | import { renderToString } from 'react-dom/server' 168 | 169 | store 170 | .dispatch(makeSandwichesForEverybody()) 171 | .then(() => response.send(renderToString())) 172 | 173 | // 也可以在任何导致组件的 props 变化的时刻 174 | // dispatch 一个异步 thunk action。 175 | 176 | import { connect } from 'react-redux' 177 | import { Component } from 'react' 178 | 179 | class SandwichShop extends Component { 180 | componentDidMount() { 181 | this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson)) 182 | } 183 | 184 | componentDidUpdate(prevProps) { 185 | if (prevProps.forPerson !== this.props.forPerson) { 186 | this.props.dispatch(makeASandwichWithSecretSauce(this.props.forPerson)) 187 | } 188 | } 189 | 190 | render() { 191 | return

{this.props.sandwiches.join('mustard')}

192 | } 193 | } 194 | 195 | export default connect(state => ({ 196 | sandwiches: state.sandwiches 197 | }))(SandwichShop) 198 | ``` 199 | 200 | #### 小贴士 201 | 202 | - Middleware 只是包装了 store 的 [`dispatch`](Store.md#dispatchaction) 方法。技术上讲,任何 middleware 能做的事情,都可能通过手动包装 `dispatch` 调用来实现,但是放在同一个地方统一管理会使整个项目的扩展变的容易得多。 203 | 204 | - 如果除了 `applyMiddleware`,你还用了其它 store enhancer,一定要把 `applyMiddleware` 放到组合链的前面,因为 middleware 可能会包含异步操作。比如,它应该在 [redux-devtools](https://github.com/reduxjs/redux-devtools) 前面,否则 DevTools 就看不到 Promise middleware 里 dispatch 的 action 了。 205 | 206 | - 如果你想有条件地使用 middleware,记住只 import 需要的部分: 207 | 208 | ```js 209 | const middleware = [a, b] 210 | if (process.env.NODE_ENV !== 'production') { 211 | const c = require('some-debug-middleware') 212 | const d = require('another-debug-middleware') 213 | middleware = [...middleware, c, d] 214 | } 215 | 216 | const store = createStore( 217 | reducer, 218 | preloadedState, 219 | applyMiddleware(...middleware) 220 | ) 221 | ``` 222 | 223 | 这样做有利于打包时去掉不需要的模块,减小打包文件的大小。 224 | 225 | - 有想过 `applyMiddleware` 本质是什么吗?它肯定是比 middleware 还强大的扩展机制。实际上,`applyMiddleware` 只是被称为 Redux 最强大的扩展机制的 [store enhancer](../understanding/thinking-in-redux/Glossary.md#store-enhancer) 中的一个范例而已。你不太可能需要实现自己的 store enhancer。另一个 store enhancer 示例是 [redux-devtools](https://github.com/reduxjs/redux-devtools)。Middleware 并没有 store enhancer 强大,但是开发起来却更容易。 226 | 227 | - Middleware 听起来比实际要复杂一些。真正理解 middleware 的唯一办法是了解现有的 middleware 是如何工作的,并尝试自己实现。需要的功能可能错综复杂,但是你会发现大部分 middleware 实际上很小,只有 10 行左右,是通过对它们的嵌套和组合使用来达到最终的目的。 228 | 229 | - 想要使用多个 store enhancer,可以使用 [`compose()`](./compose.md) 方法。 230 | -------------------------------------------------------------------------------- /docs/api/bindActionCreators.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: bindactioncreators 3 | title: bindActionCreators 4 | hide_title: true 5 | description: 'API > bindActionCreators: wrapping action creators for dispatching' 6 | --- 7 | 8 |   9 | 10 | # `bindActionCreators(actionCreators, dispatch)` 11 | 12 | 把一个 value 为不同 [action creator](../understanding/thinking-in-redux/Glossary.md#action-creator) 的对象,转成拥有同名 key 的对象。同时使用 [`dispatch`](Store.md#dispatchaction) 对每个 action creator 进行包装,以便可以直接调用它们。 13 | 14 | 一般情况下你可以直接在 [`Store`](Store.md) 实例上调用 [`dispatch`](Store.md#dispatchaction)。如果你在 React 中使用 Redux,[react-redux](https://github.com/gaearon/react-redux) 会提供 [`dispatch`](Store.md#dispatchaction) 函数让你直接调用它 。 15 | 16 | 惟一会使用到 `bindActionCreators` 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 [`dispatch`](Store.md#dispatchaction) 或 Redux store 传给它。 17 | 18 | 为方便起见,你也可以传入 action creator 作为第一个参数,并且得到一个 dispatch 函数作为返回值。 19 | 20 | #### 参数 21 | 22 | 1. `actionCreators` (_Function_ or _Object_): 一个 [action creator](../understanding/thinking-in-redux/Glossary.md#action-creator),或者一个 value 是 action creator 的对象。 23 | 24 | 2. `dispatch` (_Function_): 一个由 [`Store`](Store.md) 实例提供的 [`dispatch`](Store.md#dispatchaction) 函数。 25 | 26 | #### 返回值 27 | 28 | (_Function_ or _Object_): 一个与原对象类似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。如果传入一个单独的函数作为 `actionCreators`,那么返回的结果也是一个单独的函数。 29 | 30 | #### 示例 31 | 32 | #### `TodoActionCreators.js` 33 | 34 | ```js 35 | export function addTodo(text) { 36 | return { 37 | type: 'ADD_TODO', 38 | text 39 | } 40 | } 41 | 42 | export function removeTodo(id) { 43 | return { 44 | type: 'REMOVE_TODO', 45 | id 46 | } 47 | } 48 | ``` 49 | 50 | #### `SomeComponent.js` 51 | 52 | ```js 53 | import { Component } from 'react' 54 | import { bindActionCreators } from 'redux' 55 | import { connect } from 'react-redux' 56 | 57 | import * as TodoActionCreators from './TodoActionCreators' 58 | console.log(TodoActionCreators) 59 | // { 60 | // addTodo: Function, 61 | // removeTodo: Function 62 | // } 63 | 64 | function TodoListContainer(props) { 65 | // react-redux 注入: 66 | const { dispatch, todos } = props 67 | 68 | // 这是一个很好的 bindActionCreators 用例: 69 | // 你希望子组件完全不知道 Redux. 70 | // 我们现在创建这些函数的绑定版本,以便我们可以 71 | // 之后将它们传给子组件. 72 | 73 | const boundActionCreators = useMemo( 74 | () => bindActionCreators(TodoActionCreators, dispatch), 75 | [dispatch] 76 | ) 77 | console.log(boundActionCreators) 78 | // { 79 | // addTodo: Function, 80 | // removeTodo: Function 81 | // } 82 | 83 | useEffect(() => { 84 | // 注意: 这不起作用: 85 | // TodoActionCreators.addTodo('Use Redux') 86 | 87 | // 你只是在调用一个创建 action 的函数。 88 | // 你也必须同时 dispatch 一个 action! 89 | 90 | // 这将起到作用: 91 | let action = TodoActionCreators.addTodo('Use Redux') 92 | dispatch(action) 93 | }, []) 94 | 95 | return 96 | 97 | // bindActionCreators 的替代方法 98 | // 只有向下传递 dispatch 函数, 但是你的子组件 99 | // 需要 import action creators 并了解它们. 100 | 101 | // return 102 | } 103 | 104 | export default connect(state => ({ todos: state.todos }))(TodoListContainer) 105 | ``` 106 | 107 | #### 小贴士 108 | 109 | - 你或许要问:为什么不直接把 action creator 绑定到 store 实例上,就像传统的 Flux 那样?问题在于,这对于需要在服务端进行渲染的同构应用会有问题。多数情况下,你的每个请求都需要一个独立的 store 实例,这样你可以为它们提供不同的数据,但是如果在定义的时候绑定 action creator,你就只能使用一个唯一的 store 实例来对应所有请求了。 110 | 111 | - 如果你使用 ES5,无法使用 `import * as` 语法,你可以把 `require('./TodoActionCreators')` 作为第一个参数传给 `bindActionCreators`。惟一要注意的是作为 `actionCreators` 参数的对象,它的 value 需要是函数。模块系统并不重要。 112 | -------------------------------------------------------------------------------- /docs/api/combineReducers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: combinereducers 3 | title: combineReducers 4 | hide_title: true 5 | description: 'API > combineReducers: merging slice reducers to create combined state' 6 | --- 7 | 8 |   9 | 10 | # `combineReducers(reducers)` 11 | 12 | 随着应用变得越来越复杂,可以考虑将 [reducer 函数](../understanding/thinking-in-redux/Glossary.md#reducer) 拆分成多个单独的函数,拆分后的每个函数负责独立管理 [state](../understanding/thinking-in-redux/Glossary.md#state) 的一部分。 13 | 14 | `combineReducers` 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 [`createStore`](createStore.md) 方法。 15 | 16 | 合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 17 | **由 `combineReducers()` 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 `combineReducers()` 时对应的 key 进行命名**。 18 | 19 | 示例: 20 | 21 | ``` 22 | rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) 23 | // 这将返回如下的 state 对象 24 | { 25 | potato: { 26 | // ... potatoes, 和一些其他由 potatoReducer 管理的 state 对象 ... 27 | }, 28 | tomato: { 29 | // ... tomatoes, 和一些其他由 tomatoReducer 管理的 state 对象,比如说 sauce 属性 ... 30 | } 31 | } 32 | ``` 33 | 34 | 通过为传入对象的 reducer 命名不同的 key 来控制返回 state key 的命名。例如,你可以调用 `combineReducers({ todos: myTodosReducer, counter: myCounterReducer })` 将 state 结构变为 `{ todos, counter }`。 35 | 36 | 通常的做法是命名 reducer,然后 state 再去分割那些信息,这样你可以使用 ES6 的简写方法:`combineReducers({ counter, todos })`。这与 `combineReducers({ counter: counter, todos: todos })` 是等价的。 37 | 38 | > ##### Flux 用户使用须知 39 | > 40 | > 本函数可以帮助你组织多个 reducer,使它们分别管理自身相关联的 state。类似于 Flux 中的多个 store 分别管理不同的 state。在 Redux 中,只有一个 store,但是 `combineReducers` 让你拥有多个 reducer,同时保持各自负责逻辑块的独立性。 41 | 42 | #### 参数 43 | 44 | 1. `reducers` (_Object_): 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数需要满足的规则。 45 | 46 | > 之前的文档曾建议使用 ES6 的 `import * as reducers` 语法来获得 reducer 对象。这一点造成了很多疑问,因此现在建议在 `reducers/index.js` 里使用 `combineReducers()` 来对外输出一个 reducer。下面有示例说明。 47 | 48 | #### 返回值 49 | 50 | (_Function_):一个调用 `reducers` 对象里所有 reducer 的 reducer,并且构造一个与 `reducers` 对象结构相同的 state 对象。 51 | 52 | #### 注意 53 | 54 | 本函数设计的时候有点偏主观,就是为了避免新手犯一些常见错误。也因些我们故意设定一些规则,但如果你自己手动编写根 redcuer 时,并不需要遵守这些规则。 55 | 56 | 每个传入 `combineReducers` 的 reducer 都需满足以下规则: 57 | 58 | - 所有未匹配到的 action,必须把它接收到的第一个参数也就是那个 `state` 原封不动返回。 59 | 60 | - 永远不能返回 `undefined`。当过早 `return` 时非常容易犯这个错误,为了避免错误扩散,遇到这种情况时 `combineReducers` 会抛异常。 61 | 62 | - 如果传入的 `state` 就是 `undefined`,一定要返回对应 reducer 的初始 state。根据上一条规则,初始 state 禁止使用 `undefined`。使用 ES6 的默认参数值语法来设置初始 state 很容易,但你也可以手动检查第一个参数是否为 `undefined`。 63 | 64 | 虽然 `combineReducers` 自动帮你检查 reducer 是否符合以上规则,但你也应该牢记,并尽量遵守。即使你通过 `Redux.createStore(combineReducers(...), initialState)` 指定初始 state,`combineReducers` 也会尝试通过传递 `undefined` 的 `state` 来检测你的 reducer 是否符合规则。因此,即使你在代码中不打算实际接收值为 `undefined` 的 `state`,也**必须**保证你的 reducer 在接收到 `undefined` 时能够正常工作。 65 | 66 | #### 示例 67 | 68 | #### `reducers/todos.js` 69 | 70 | ```js 71 | export default function todos(state = [], action) { 72 | switch (action.type) { 73 | case 'ADD_TODO': 74 | return state.concat([action.text]) 75 | default: 76 | return state 77 | } 78 | } 79 | ``` 80 | 81 | #### `reducers/counter.js` 82 | 83 | ```js 84 | export default function counter(state = 0, action) { 85 | switch (action.type) { 86 | case 'INCREMENT': 87 | return state + 1 88 | case 'DECREMENT': 89 | return state - 1 90 | default: 91 | return state 92 | } 93 | } 94 | ``` 95 | 96 | #### `reducers/index.js` 97 | 98 | ```js 99 | import { combineReducers } from 'redux' 100 | import todos from './todos' 101 | import counter from './counter' 102 | 103 | export default combineReducers({ 104 | todos, 105 | counter 106 | }) 107 | ``` 108 | 109 | #### `App.js` 110 | 111 | ```js 112 | import { createStore } from 'redux' 113 | import reducer from './reducers/index' 114 | 115 | let store = createStore(reducer) 116 | console.log(store.getState()) 117 | // { 118 | // counter: 0, 119 | // todos: [] 120 | // } 121 | 122 | store.dispatch({ 123 | type: 'ADD_TODO', 124 | text: 'Use Redux' 125 | }) 126 | console.log(store.getState()) 127 | // { 128 | // counter: 0, 129 | // todos: [ 'Use Redux' ] 130 | // } 131 | ``` 132 | 133 | #### 小贴士 134 | 135 | - 本方法只是起辅助作用!你可以自行实现[不同功能](https://github.com/acdlite/reduce-reducers)的 `combineReducers`,甚至像实现其它函数一样,明确地写一个根 reducer 函数,用它把子 reducer 手动组装成 state 对象。 136 | 137 | - 在 reducer 层级的任何一级都可以调用 `combineReducers`。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer,甚至更多层。 138 | -------------------------------------------------------------------------------- /docs/api/compose.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: compose 3 | title: compose 4 | hide_title: true 5 | description: 'API > compose: composing multiple functions together' 6 | --- 7 | 8 |   9 | 10 | # `compose(...functions)` 11 | 12 | 从右到左来组合多个函数。 13 | 14 | 这是函数式编程中的方法,为了方便,被放到了 Redux 里。 15 | 当需要把多个 [store enhancers](../understanding/thinking-in-redux/Glossary.md#store-enhancer) 依次执行的时候,需要用到它。 16 | 17 | #### 参数 18 | 19 | 1. (_arguments_): 需要合成的多个函数。预计每个函数都接收一个参数。它的返回值将作为一个参数提供给它左边的函数,以此类推。例外是最右边的参数可以接受多个参数,因为它将为由此产生的函数提供签名。(译者注:`compose(funcA, funcB, funcC)` 形象为 `compose(funcA(funcB(funcC())))`) 20 | 21 | #### 返回值 22 | 23 | (_Function_): 从右到左把接收到的函数合成后的最终函数。 24 | 25 | #### 示例 26 | 27 | 下面示例演示了如何使用 `compose` 增强 [store](Store.md),这个 store 与 [`applyMiddleware`](applyMiddleware.md) 和 [redux-devtools](https://github.com/reduxjs/redux-devtools) 一起使用。 28 | 29 | ```js 30 | import { createStore, applyMiddleware, compose } from 'redux' 31 | import thunk from 'redux-thunk' 32 | import DevTools from './containers/DevTools' 33 | import reducer from '../reducers' 34 | 35 | const store = createStore( 36 | reducer, 37 | compose( 38 | applyMiddleware(thunk), 39 | DevTools.instrument() 40 | ) 41 | ) 42 | ``` 43 | 44 | #### 小贴士 45 | 46 | - 所有的 `compose` 做的只是让你在写深度嵌套的函数时,避免了代码的向右偏移(译者注:可以参考[上述的译者注](#参数))。不要觉得它很复杂。 47 | -------------------------------------------------------------------------------- /docs/api/createStore.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: createstore 3 | title: createStore 4 | hide_title: true 5 | description: 'API > createStore: creating a core Redux store' 6 | --- 7 | 8 |   9 | 10 | # `createStore(reducer, [preloadedState], [enhancer])` 11 | 12 | 创建一个包含程序完整 state 树的 Redux [store](Store.md) 。 13 | 应用中应有且仅有一个 store。 14 | 15 | #### 参数 16 | 17 | 1. `reducer` _(Function)_: 接收两个参数,分别是当前的 state 树和要处理的 [action](../understanding/thinking-in-redux/Glossary.md#action),返回新的 [state 树](../understanding/thinking-in-redux/Glossary.md#state)。 18 | 19 | 2. [`preloadedState`] _(any)_: 初始时的 state。你可以决定是否把服务端传来的 state 水合(hydrate)后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 [`combineReducers`](combineReducers.md) 创建 `reducer`,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 `reducer` 可理解的内容。 20 | 21 | 3. `enhancer` _(Function)_: Store enhancer。你可以选择指定它以使用第三方功能,如middleware、时间旅行、持久化来增强 store。Redux 中唯一内置的 store enhander 是 [`applyMiddleware()`](./applyMiddleware.md)。 22 | 23 | #### 返回值 24 | 25 | ([_`Store`_](Store.md)): 保存了应用程序所有 state 的对象。改变 state 的惟一方法是 [dispatch action](Store.md#dispatchaction)。你也可以 [subscribe](Store.md#subscribelistener) state 的变化,然后更新 UI。 26 | 27 | #### 示例 28 | 29 | ```js 30 | import { createStore } from 'redux' 31 | 32 | function todos(state = [], action) { 33 | switch (action.type) { 34 | case 'ADD_TODO': 35 | return state.concat([action.text]) 36 | default: 37 | return state 38 | } 39 | } 40 | 41 | const store = createStore(todos, ['Use Redux']) 42 | 43 | store.dispatch({ 44 | type: 'ADD_TODO', 45 | text: 'Read the docs' 46 | }) 47 | 48 | console.log(store.getState()) 49 | // [ 'Use Redux', 'Read the docs' ] 50 | ``` 51 | 52 | #### 小贴士 53 | 54 | - 应用中不要创建多个 store!相反,使用 [`combineReducers`](combineReducers.md) 来把多个 reducer 创建成一个根 reducer。 55 | 56 | - Redux state 通常是普通 JS 对象或者数组。 57 | 58 | - 如果 state 是普通对象,永远不要修改它!不可变更新需要复制每个级别的数据,通常使用对象扩展运算符(`return { ...state, ...newData }`)。 59 | 60 | - 对于服务端运行的同构应用,为每一个请求创建一个 store 实例,以此让 store 相隔离。dispatch 一系列请求数据的 action 到 store 实例上,等待请求完成后再在服务端渲染应用。 61 | 62 | - 当 store 创建后,Redux 会 dispatch 一个 action 到 reducer 上,来用初始的 state 来填充 store。你不需要处理这个 action。但要记住,如果第一个参数也就是传入的 state 是 `undefined` 的话,reducer 应该返回初始的 state 值。 63 | 64 | - 要使用多个 store enhancer 的时候,你可能需要使用 [compose](./compose.md) 65 | -------------------------------------------------------------------------------- /docs/components/DetailedExplanation.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const DetailedExplanation = ({ 4 | children, 5 | title = 'Detailed Explanation' 6 | }) => { 7 | return ( 8 |
9 | 10 |

{title}

11 |
12 | {children} 13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /docs/faq/DesignDecisions.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: design-decisions 3 | title: Design Decisions 4 | hide_title: false 5 | --- 6 | 7 | # Redux FAQ: 设计决策 8 | 9 | ## 目录 10 | 11 | - [为什么 Redux 不将 state 和 action 传递给订阅者?](#why-doesnt-redux-pass-the-state-and-action-to-subscribers) 12 | - [为什么 Redux 不支持对 action 和 reducer 使用类?](#why-doesnt-redux-support-using-classes-for-actions-and-reducers) 13 | - [为什么 middleware 签名使用柯里化?](#why-does-the-middleware-signature-use-currying) 14 | - [为什么 applyMiddleware 使用闭包进行 dispatch?](#why-does-applymiddleware-use-a-closure-for-dispatch) 15 | - [为什么 `combineReducers` 在调用每个 reducer 时不包含整个 state 的第三个参数?](#why-doesnt-combinereducers-include-a-third-argument-with-the-entire-state-when-it-calls-each-reducer) 16 | - [为什么 mapDispatchToProps 不允许使用来自 `getState()` 或 `mapStateToProps()` 的返回值?](#why-doesnt-mapdispatchtoprops-allow-use-of-return-values-from-getstate-or-mapstatetoprops) 17 | 18 | ## 设计决策 19 | 20 | ### 为什么 Redux 不将 state 和 action 传递给订阅者? 21 | 22 | 订阅者旨在响应 state 值本身,而不是 action。对 state 的更新是同步处理的,但是给订阅者的通知可以被批处理或去抖动,这意味着订阅者并不总是被通知每个 action。这是一种常见的[性能优化](./Performance.md#performance-update-events),以避免重复重新渲染。 23 | 24 | 通过使用增强器覆盖 “store.dispatch” 来改变通知订阅者的方式,可以进行批处理或去抖动。此外,还有一些库将 Redux 更改为批量处理操作以优化性能并避免重复重新渲染: 25 | 26 | - [redux-batch](https://github.com/manaflair/redux-batch) 只需要一个通知,将一组操作传递给 `store.dispatch()`。 27 | - [redux-batched-subscribe](https://github.com/tappleby/redux-batched-subscribe) 允许批处理由于 dispatch 而发生的订阅通知。 28 | 29 | 预期的保证是 Redux 最终以可用的最新 state 调用所有订阅者,但并不总是为每个 action 调用每个订阅者。只需调用 store.getState() 即可在订阅者中获得存储 state。如果不破坏可能对 action 进行批处理的方式,则无法在订阅者中使用该 action。 30 | 31 | 在订阅者内部使用该 action 的潜在用例(这是一个不受支持的功能)是确保组件仅在某些类型的 action 后重新呈现。相反,应通过以下方式控制重新渲染: 32 | 33 | 1. the [shouldComponentUpdate](https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate) 生命周期方法 34 | 2. the [virtual DOM equality check (vDOMEq)](https://facebook.github.io/react/docs/optimizing-performance.html#avoid-reconciliation) 35 | 3. [React.PureComponent](https://facebook.github.io/react/docs/optimizing-performance.html#examples) 36 | 4. Using React-Redux: use [mapStateToProps](https://react-redux.js.org/api#connect) 将组件订阅到他们需要的 store 部分 37 | 38 | #### 更多信息 39 | 40 | **文章** 41 | 42 | - [如何减少 store 更新事件的数量?](./Performance.md#performance-update-events) 43 | 44 | **讨论** 45 | 46 | - [#580: 为什么 Redux 不将 state 传递给订阅者?](https://github.com/reactjs/redux/issues/580) 47 | - [#2214: Alternate Proof of Concept: Enhancer Overhaul -- more on debouncing](https://github.com/reactjs/redux/pull/2214) 48 | 49 | ### 为什么 Redux 不支持对 action 和 reducer 使用类? 50 | 51 | 对于具有大量面向对象编程经验的程序员来说,使用称为 action creator 的函数返回动作对象的模式似乎违反直觉,他们会认为这是类和实例的强大用例。不支持 action 对象和 reducer 的类实例,因为类实例使序列化和反序列化变得棘手。像 `JSON.parse(string)` 这样的反序列化方法将返回一个普通的旧 Javascript 对象而不是类实例。 52 | 53 | 如 [Store FAQ](./OrganizingState.md#organizing-state-non-serializable) 中所述,如果您可以接受诸如持久性和时间旅行调试之类的事情无法按预期工作,欢迎你也可以在 Redux store 使用非序列化对象。 54 | 55 | 序列化使浏览器能够以更少的内存存储所有已 dispatch 的 action,以及之前的store state。回退和 “热重新加载” store 是 Redux 开发人员体验和 Redux DevTools 功能的核心。这也使得反序列化的操作能够存储在服务器上,并在使用 Redux 进行服务器端渲染的情况下在浏览器中重新序列化。 56 | 57 | #### 更多信息 58 | 59 | **文章** 60 | 61 | - [我可以将函数,promises,或其他不可序列化的项目放在 store state 中吗?](./OrganizingState.md#organizing-state-non-serializable) 62 | 63 | **讨论** 64 | 65 | - [#1171: 为什么 Redux 不使用类作为 action 和 reducer?](https://github.com/reactjs/redux/issues/1171#issuecomment-196819727) 66 | 67 | ### 为什么 middleware 签名使用柯里化? 68 | 69 | 为什么 Redux 不使用类作为 action 和 reducer?Redux middleware 是使用三重嵌套的函数结构编写的,看起来像 `const middleware = storeAPI => next => action => {}`,而不是看起来像的单个函数 比如`const middleware = (storeAPI, next, action) => {}`。以下有几个原因。 70 | 71 | 其中一个原因是 “currying” 函数是一种标准的函数式编程技术,Redux明确打算在其设计中使用函数式编程原则。另一个原因是 currying 函数创建闭包,你可以在闭包中声明 middleware 生存期内存在的变量(可以将其视为与类实例生存期内的实例变量等效的函数)。最后,它是最初设计 Redux 时选择的方法。 72 | 73 | 声明 middleware 的 [柯里化的函数签名](https://github.com/reactjs/redux/issues/1744) 被某些人[认为没有必要](https://github.com/reactjs/redux/pull/784),因为执行 applyMiddleware 函数时 store 和 next 都可用。这个问题已被确定为不 [值得引入重大更改](https://github.com/reactjs/redux/issues/1744),因为现在 Redux 生态系统中有数百个 middleware 依赖于现有的 middleware 定义 . 74 | 75 | #### 更多信息 76 | 77 | **讨论** 78 | 79 | - 为什么 middleware 签名使用柯里化? 80 | - 之前的讨论: [#55](https://github.com/reactjs/redux/pull/55),[#534](https://github.com/reactjs/redux/issues/534),[#784](https://github.com/reactjs/redux/pull/784),[#922](https://github.com/reactjs/redux/issues/922),[#1744](https://github.com/reactjs/redux/issues/1744) 81 | - [React Boston 2017: 你可能需要 Redux(及其生态系统)](https://blog.isquaredsoftware.com/2017/09/presentation-might-need-redux-ecosystem/) 82 | 83 | ### 为什么 applyMiddleware 使用闭包进行 dispatch? 84 | 85 | `applyMiddleware` 从 store 中获取现有的 dispatch 并关闭它以创建初始 middleware 链,这些 middleware 已使用公开 getState 和 dispatch 函数的对象调用,这使得 middleware [在初始化期间依赖dispatch](https: //github.com/reactjs/redux/pull/1592) 运行。 86 | 87 | #### 更多信息 88 | 89 | **讨论** 90 | 91 | - 为什么 applyMiddleware 使用闭包进行 dispatch? 92 | - 见 - [#1592](https://github.com/reactjs/redux/pull/1592) 和 [#2097](https://github.com/reactjs/redux/issues/2097) 93 | 94 | ### 为什么 `combineReducers` 在调用每个 reducer 时不包含整个 state 的第三个参数? 95 | 96 | `combineReducers` 被认为是鼓励按域拆分 reducer 逻辑。正如 [Beyond `combineReducers`](../recipes/structuring-reducers/BeyondCombineReducers.md) 中所述,`combineReducers` 被故意限制为处理单个常见用例:通过委托更新作为普通 Javascript 对象的 state 树,将每个 state 切片更新为特定切片 reducer。 97 | 98 | 每个 reducer 的潜在第三个参数应该是什么并不是很明显,可以为:整个 state 树、一些回调函数、state 树的其他部分等。如果 `combineReducers` 不适合你的用例,请考虑使用类似的库 [combineSectionReducers](https://github.com/ryo33/combine-section-reducers) 或 [reduceReducers](https://github.com/acdlite/reduce-reducers) 用于具有深度嵌套的 reducer 和需要 reducer 的其他选项访问全局状态。 99 | 100 | 如果已发布的实用程序都不能解决你的用例,你始终可以自己编写一个完全满足你需要的函数。 101 | 102 | #### 更多信息 103 | 104 | **文章** 105 | 106 | - [Beyond `combineReducers`](../recipes/structuring-reducers/BeyondCombineReducers.md) 107 | 108 | **讨论** 109 | 110 | - [#1768 允许 reducer 查询全局状态](https://github.com/reactjs/redux/pull/1768) 111 | 112 | ### 为什么 mapDispatchToProps 不允许使用来自 `getState()` 或 `mapStateToProps()` 的返回值? 113 | 114 | 如果已经有请求在 `mapDispatch` 中使用整个 `state` 或 `mapState` 的返回值中,当在 `mapDispatch` 中声明函数时,它们可以关闭来自 store 的最新返回值。 115 | 116 | `mapDispatch` 不支持这种方法,因为这意味着每次更新 store 时也要调用 `mapDispatch`。这将导致在每次 state 更新时重新创建函数,从而增加大量性能开销。 117 | 118 | 处理这个用例的首选方法——需要根据当前 state 和 mapDispatchToProps 函数来改变 props——使用 mergeProps,connect 函数的第三个参数。如果指定,则传递 `mapStateToProps()`、`mapDispatchToProps()` 和容器组件的 props 的结果。从 `mergeProps` 返回的普通对象将作为道具传递给包装的组件。 119 | 120 | #### 更多信息 121 | 122 | **讨论** 123 | 124 | - [#237 为什么 mapDispatchToProps 不允许使用 getState() 或 mapStateToProps()?](https://github.com/reactjs/react-redux/issues/237) 125 | -------------------------------------------------------------------------------- /docs/faq/General.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: general 3 | title: General 4 | hide_title: false 5 | --- 6 | 7 | # Redux FAQ: 常见问题 8 | 9 | ## 我应该什么时候学习 Redux? 10 | 11 | 对于 JavaScript 开发人员来说,学习什么可能是一个压倒性的问题。可以通过你在工作中发现的问题并专注于一次学习一种知识来缩小选择范围。Redux 是一种用于管理应用程序状态的模式。如果你在状态管理方面没有问题,你可能对于 Redux 的好处更难理解。一些 UI 库(如 React)有自己的状态管理系统。如果你正在使用其中一个库,特别是如果刚刚学习使用它们,我们鼓励你首先了解该内置系统的功能。这可能是你构建应用程序所需的全部内容。如果你的应用程序变得很复杂以至于你对状态存储在哪里或状态如何变化感到困惑,那么现在是学习 Redux 的好时机。 12 | 13 | :::tip 14 | 15 | **我们建议大多数新学习者应该先专注于学习 React,等到熟悉 React 后再学习 Redux**。这样,一次学习的新概念就更少了,并且更清楚哪些概念是 React 的一部分,哪些概念是 Redux 的一部分。你还将更好地了解如何将 Redux 用于 React 应用程序,以及 Redux 为何有用。 16 | 17 | ::: 18 | 19 | #### 更多信息 20 | 21 | **文章** 22 | 23 | - [决定不学什么](https://gedd.ski/post/what-not-to-learn/) 24 | - [如何学习网络框架](https://ux.shopify.com/how-to-learn-web-frameworks-9d447cb71e68) 25 | - [Redux vs MobX vs Flux vs... 你甚至需要那个吗?](https://goshakkk.name/redux-vs-mobx-vs-flux-etoomanychoices/) 26 | 27 | **讨论** 28 | 29 | - [Ask HN: 学习前端不知所措,我该如何进行?](https://news.ycombinator.com/item?id=12882816) 30 | - [Twitter: 如果你想教某人使用抽象...](https://twitter.com/acemarke/status/901329101088215044) 31 | - [Twitter: 以前从来没有打算学过...](https://twitter.com/dan_abramov/status/739961787295117312) 32 | - [Twitter: 在 React 之前学习 Redux?](https://twitter.com/dan_abramov/status/739962098030137344) 33 | - [Twitter: 我第一次使用 React 时,人们告诉我需要 Redux ......](https://twitter.com/raquelxmoss/status/901576285020856320) 34 | - [Twitter: 这是我对 Redux 的体验...](https://twitter.com/garetmckinley/status/901500556568645634) 35 | - [Dev.to: 什么时候可以使用 Redux?](https://dev.to/dan_abramov/comment/1n2k) 36 | 37 | ## 我什么时候应该使用 Redux? 38 | 39 | **并非所有应用都需要 Redux。了解你正在构建的应用程序类型、需要解决的问题类型以及哪些工具可以最好地解决你面临的问题非常重要。** 40 | 41 | Redux 可帮助你处理共享状态管理,但与任何工具一样,它也需要权衡取舍。它并非旨在成为编写代码的最短或最快方式。它旨在通过可预测的行为来帮助回答“某部分状态何时发生变化,数据来自何处?”的问题。有更多的概念要学习,也有更多的代码要编写。它还为你的代码添加了一些间接性,并要求你遵循某些限制。这是短期和长期生产力之间的权衡。 42 | 43 | 正如 React 的早期贡献者之一 Pete Hunt 所说: 44 | 45 | > 你会知道何时需要 Flux。如果你不确定你是否需要它,你就不需要它。 46 | 47 | 同样,Redux 的创建者之一 Dan Abramov 说: 48 | 49 | > 我想修正这一点:在你对 vanilla React 有问题之前不要使用 Redux。 50 | 51 | **Redux 在以下情况下最有用**: 52 | 53 | - 你在应用程序的许多地方都需要大量的应用程序状态 54 | - 应用状态更新频繁 55 | - 更新该状态的逻辑可能很复杂 56 | - 该应用程序有一个中型或大型的代码库,并且可能由许多人开发 57 | - 你需要查看该状态如何随着时间的推移而更新 58 | 59 | 还有许多其他工具可以帮助解决 Redux 遇到的一些相同问题:状态管理、缓存获取的服务器数据以及通过 UI 传递数据。 60 | 61 | :::info 62 | 63 | 如果你不确定 Redux 是否适合你的应用程序,这些资源会提供更多指导: 64 | 65 | - **[何时(以及何时不)接触 Redux](https://changelog.com/posts/when-and-when-not-to-reach-for-redux)** 66 | - **[Redux 之道,第 1 部分 - 实现和意图](https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/)** 67 | - **[你或许不需要 Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367)** 68 | 69 | ::: 70 | 71 | 归根结底,Redux 只是一个工具。它是一个很棒的工具,使用它有一些很好的理由,但也有一些你可能不想使用它的理由。对你的工具做出明智的决策,并了解每个决策中涉及的权衡。 72 | 73 | #### 更多信息 74 | 75 | **文档** 76 | 77 | - [Redux 中的思考: 动机](../understanding/thinking-in-redux/Motivation.md) 78 | 79 | **文章** 80 | 81 | - **[何时(以及何时不)接触 Redux](https://changelog.com/posts/when-and-when-not-to-reach-for-redux)** 82 | - **[Redux 之道,第 1 部分 - 实现和意图](https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/)** 83 | - [你或许不需要 Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367) 84 | - [Flux 示例](https://medium.com/swlh/the-case-for-flux-379b7d1982c6) 85 | 86 | **讨论** 87 | 88 | - [Twitter: 不要用 Redux 除非...](https://twitter.com/dan_abramov/status/699241546248536064) 89 | - [Twitter: Redux 的设计是可预测的,而不是简洁的](https://twitter.com/dan_abramov/status/733742952657342464) 90 | - [Twitter: Redux 对于消除深度 prop 传递很有用](https://twitter.com/dan_abramov/status/732912085840089088) 91 | - [Twitter: 除非你对本地组件 state 不满意,否则不要使用 Redux](https://twitter.com/dan_abramov/status/725089243836588032) 92 | - [Twitter: 如果你的数据从不改变,你就不需要 Redux](https://twitter.com/dan_abramov/status/737036433215610880) 93 | - [Twitter: 如果你的 reducer 看起来很单调,不要使用 redux](https://twitter.com/dan_abramov/status/802564042648944642) 94 | - [Reddit: 如果你的应用只是在单个页面上获取某些内容,则不需要 Redux](https://www.reddit.com/r/reactjs/comments/5exfea/feedback_on_my_first_redux_app/dagglqp/) 95 | - [Stack Overflow: 为什么使用 Redux 而不是 Facebook Flux?](https://stackoverflow.com/questions/32461229/why-use-redux-over-facebook-flux) 96 | - [Stack Overflow: 为什么我应该在这个例子中使用 Redux?](https://stackoverflow.com/questions/35675339/why-should-i-use-redux-in-this-example) 97 | - [Stack Overflow: 使用 Redux 而不是 Flux 的缺点是什么?](https://stackoverflow.com/questions/32021763/what-could-be-the-downsides-of-using-redux-instead-of-flux) 98 | - [Stack Overflow: 我应该何时将 Redux 添加到 React 应用程序?](https://stackoverflow.com/questions/36631761/when-should-i-add-redux-to-a-react-app) 99 | - [Stack Overflow: Redux 与普通 React?](https://stackoverflow.com/questions/39260769/redux-vs-plain-react/39261546#39261546) 100 | - [Twitter: Redux 是一个供开发人员使用可重用事物构建自定义状态管理的平台](https://twitter.com/acemarke/status/793862722253447168) 101 | 102 | ## Redux 只能与 React 一起使用吗? 103 | 104 | Redux 可以用作任何 UI 层的数据存储。最常见的用法是使用 React 和 React Native,但也有适用于 Angular、Angular 2、Vue、Mithril 等的绑定。Redux 只是提供了一种订阅机制,任何其他代码都可以使用它。也就是说,当与可以从状态更改推断 UI 更新的声明性视图库结合使用时,它是最有用的,例如 React 或可用的类似库之一。 105 | 106 | ## 我是否需要特定的构建工具才能使用 Redux? 107 | 108 | Redux 最初是用 ES6 编写的,并使用 Webpack 和 Babel 转译为 ES5 以用于生产。无论你的 JavaScript 构建过程如何,你都应该能够使用它。Redux 还提供了一个 UMD 构建,可以直接使用,而无需任何构建过程。[counter-vanilla](https://github.com/reduxjs/redux/tree/master/examples/counter-vanilla) 示例演示了基本的 ES5 用法,其中 Redux 包含在 `