├── .codesandbox └── ci.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .git-blame-ignore-revs ├── .gitattributes ├── .gitbook.yaml ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ ├── Feature_request.md │ ├── config.yml │ ├── documentation-edit.md │ └── documentation-new.md ├── PULL_REQUEST_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE │ ├── bugfix.md │ ├── documentation-edit.md │ └── documentation-new.md └── workflows │ ├── publish.yaml │ ├── size.yaml │ └── test.yaml ├── .gitignore ├── .prettierrc.json ├── .release-it.json ├── .yarn └── releases │ └── yarn-4.4.1.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── CNAME ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE-logo.md ├── LICENSE.md ├── PATRONS.md ├── README.md ├── docs ├── FAQ.md ├── api │ ├── Store.md │ ├── api-reference.md │ ├── applyMiddleware.md │ ├── bindActionCreators.md │ ├── combineReducers.md │ ├── compose.md │ ├── createStore.md │ └── utils.md ├── components │ ├── DetailedExplanation.jsx │ └── _FundamentalsWarning.mdx ├── 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 │ ├── README.md │ └── why-rtk-is-redux-today.md ├── package.json ├── 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 │ │ ├── history-of-redux.md │ │ └── middleware.md │ └── thinking-in-redux │ │ ├── Glossary.md │ │ ├── Motivation.md │ │ └── ThreePrinciples.md ├── usage │ ├── CodeSplitting.md │ ├── ConfiguringYourStore.md │ ├── ImplementingUndoHistory.md │ ├── IsolatingSubapps.md │ ├── ReducingBoilerplate.md │ ├── ServerRendering.md │ ├── Troubleshooting.md │ ├── UsageWithTypescript.md │ ├── WritingCustomMiddleware.md │ ├── WritingTests.mdx │ ├── deriving-data-selectors.md │ ├── index.md │ ├── migrating-to-modern-redux.mdx │ ├── migrations │ │ └── migrating-rtk-2.md │ ├── nextjs.mdx │ ├── side-effects-approaches.mdx │ ├── 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 └── yarn.lock ├── errors.json ├── examples ├── README.md ├── async │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ └── index.js │ │ ├── components │ │ ├── Picker.js │ │ └── Posts.js │ │ ├── containers │ │ └── App.js │ │ ├── index.js │ │ └── reducers │ │ └── index.js ├── counter-ts │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo.svg │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── app │ │ │ ├── hooks.ts │ │ │ └── store.ts │ │ ├── features │ │ │ └── counter │ │ │ │ ├── Counter.module.css │ │ │ │ ├── Counter.tsx │ │ │ │ ├── counterAPI.ts │ │ │ │ ├── counterSlice.spec.ts │ │ │ │ └── counterSlice.ts │ │ ├── index.css │ │ ├── index.tsx │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ └── setupTests.ts │ └── tsconfig.json ├── counter-vanilla │ ├── README.md │ ├── index.html │ ├── package-lock.json │ └── package.json ├── counter │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo.svg │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── app │ │ └── store.js │ │ ├── features │ │ └── counter │ │ │ ├── Counter.js │ │ │ ├── Counter.module.css │ │ │ ├── counterAPI.js │ │ │ ├── counterSlice.js │ │ │ └── counterSlice.spec.js │ │ ├── index.css │ │ ├── index.js │ │ ├── reportWebVitals.js │ │ └── setupTests.js ├── real-world │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ └── index.js │ │ ├── components │ │ ├── Explore.js │ │ ├── List.js │ │ ├── Repo.js │ │ └── User.js │ │ ├── containers │ │ ├── App.js │ │ ├── DevTools.js │ │ ├── RepoPage.js │ │ ├── Root.dev.js │ │ ├── Root.js │ │ ├── Root.prod.js │ │ └── UserPage.js │ │ ├── index.js │ │ ├── middleware │ │ └── api.js │ │ ├── reducers │ │ ├── index.js │ │ └── paginate.js │ │ └── store │ │ ├── configureStore.dev.js │ │ ├── configureStore.js │ │ └── configureStore.prod.js ├── shopping-cart │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ └── index.js │ │ ├── api │ │ ├── products.json │ │ └── shop.js │ │ ├── components │ │ ├── Cart.js │ │ ├── Cart.spec.js │ │ ├── Product.js │ │ ├── Product.spec.js │ │ ├── ProductItem.js │ │ ├── ProductItem.spec.js │ │ ├── ProductsList.js │ │ └── ProductsList.spec.js │ │ ├── constants │ │ └── ActionTypes.js │ │ ├── containers │ │ ├── App.js │ │ ├── CartContainer.js │ │ └── ProductsContainer.js │ │ ├── index.js │ │ ├── reducers │ │ ├── cart.js │ │ ├── cart.spec.js │ │ ├── index.js │ │ ├── index.spec.js │ │ ├── products.js │ │ └── products.spec.js │ │ └── setupTests.js ├── testAll.js ├── todomvc │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ ├── index.js │ │ └── index.spec.js │ │ ├── components │ │ ├── App.js │ │ ├── App.spec.js │ │ ├── Footer.js │ │ ├── Footer.spec.js │ │ ├── Header.js │ │ ├── Header.spec.js │ │ ├── Link.js │ │ ├── Link.spec.js │ │ ├── MainSection.js │ │ ├── MainSection.spec.js │ │ ├── TodoItem.js │ │ ├── TodoItem.spec.js │ │ ├── TodoList.js │ │ ├── TodoList.spec.js │ │ ├── TodoTextInput.js │ │ └── TodoTextInput.spec.js │ │ ├── constants │ │ ├── ActionTypes.js │ │ └── TodoFilters.js │ │ ├── containers │ │ ├── FilterLink.js │ │ ├── Header.js │ │ ├── MainSection.js │ │ └── VisibleTodoList.js │ │ ├── index.js │ │ ├── reducers │ │ ├── index.js │ │ ├── todos.js │ │ ├── todos.spec.js │ │ └── visibilityFilter.js │ │ └── selectors │ │ └── index.js ├── todos-with-undo │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ └── index.js │ │ ├── components │ │ ├── App.js │ │ ├── Footer.js │ │ ├── Link.js │ │ ├── Todo.js │ │ └── TodoList.js │ │ ├── containers │ │ ├── AddTodo.js │ │ ├── FilterLink.js │ │ ├── UndoRedo.js │ │ └── VisibleTodoList.js │ │ ├── index.js │ │ └── reducers │ │ ├── index.js │ │ ├── todos.js │ │ └── visibilityFilter.js ├── todos │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ ├── index.js │ │ └── index.spec.js │ │ ├── components │ │ ├── App.js │ │ ├── Footer.js │ │ ├── Link.js │ │ ├── Todo.js │ │ └── TodoList.js │ │ ├── containers │ │ ├── AddTodo.js │ │ ├── FilterLink.js │ │ └── VisibleTodoList.js │ │ ├── index.js │ │ └── reducers │ │ ├── index.js │ │ ├── todos.js │ │ ├── todos.spec.js │ │ └── visibilityFilter.js ├── tree-view │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── actions │ │ └── index.js │ │ ├── containers │ │ ├── Node.js │ │ └── Node.spec.js │ │ ├── generateTree.js │ │ ├── index.js │ │ ├── reducers │ │ ├── index.js │ │ └── index.spec.js │ │ └── setupTests.js └── universal │ ├── .babelrc │ ├── README.md │ ├── client │ └── index.js │ ├── common │ ├── actions │ │ └── index.js │ ├── api │ │ └── counter.js │ ├── components │ │ └── Counter.js │ ├── containers │ │ └── App.js │ ├── reducers │ │ ├── counter.js │ │ └── index.js │ └── store │ │ └── configureStore.js │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── server │ ├── index.js │ └── server.js │ └── webpack.config.js ├── logo ├── README.md ├── apple-touch-icon.png ├── favicon.ico ├── logo-title-dark.png ├── logo-title-light.png ├── logo.png └── logo.svg ├── netlify.toml ├── package.json ├── scripts └── mangleErrors.mts ├── src ├── applyMiddleware.ts ├── bindActionCreators.ts ├── combineReducers.ts ├── compose.ts ├── createStore.ts ├── index.ts ├── types │ ├── actions.ts │ ├── middleware.ts │ ├── reducers.ts │ └── store.ts └── utils │ ├── actionTypes.ts │ ├── formatProdErrorMessage.ts │ ├── isAction.ts │ ├── isPlainObject.ts │ ├── kindOf.ts │ ├── symbol-observable.ts │ └── warning.ts ├── test ├── applyMiddleware.spec.ts ├── bindActionCreators.spec.ts ├── combineReducers.spec.ts ├── compose.spec.ts ├── createStore.spec.ts ├── helpers │ ├── actionCreators.ts │ ├── actionTypes.ts │ ├── middleware.ts │ └── reducers.ts ├── typescript │ ├── .eslintrc.cjs │ ├── actionCreators.test-d.ts │ ├── actions.test-d.ts │ ├── compose.test-d.ts │ ├── dispatch.test-d.ts │ ├── enhancers.test-d.ts │ ├── injectedDispatch.test-d.ts │ ├── middleware.test-d.ts │ ├── reducers.test-d.ts │ └── store.test-d.ts └── utils │ ├── formatProdErrorMessage.spec.ts │ ├── isAction.spec.ts │ ├── isPlainObject.spec.ts │ └── warning.spec.ts ├── tsconfig.base.json ├── tsconfig.build.json ├── tsconfig.json ├── tsconfig.test.json ├── tsup.config.ts ├── vitest.config.mts ├── website ├── .gitignore ├── README.md ├── _redirects ├── docusaurus.config.ts ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ ├── js │ │ └── monokaiTheme.js │ └── pages │ │ ├── errors.js │ │ ├── index.js │ │ └── styles.module.css ├── static │ └── img │ │ ├── cogs-solid.svg │ │ ├── course-callout-mid.svg │ │ ├── course-callout-narrow.svg │ │ ├── course-callout-wide.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-basic-counter.png │ │ ├── devtools-cached-invalidation-refetching.png │ │ ├── devtools-cached-requests.png │ │ ├── devtools-done-clicking.png │ │ ├── devtools-first-action.png │ │ ├── devtools-initial.png │ │ ├── devtools-posts-duplicate.png │ │ ├── devtools-posts-fulfilled.png │ │ ├── devtools-posts-pending.png │ │ ├── devtools-rtkq-cache.png │ │ ├── devtools-rtkq-tab.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-duplicates.png │ │ ├── posts-fetched.png │ │ ├── posts-unknownAuthor.png │ │ ├── postslist-optimized.png │ │ ├── postslist-rerender.png │ │ ├── userpage-optimized.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 ├── tsconfig.json └── yarn.lock └── yarn.lock /.codesandbox/ci.json: -------------------------------------------------------------------------------- 1 | { 2 | "sandboxes": ["vanilla", "vanilla-ts"], 3 | "node": "20" 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain 2 | # consistent coding styles between different editors and IDEs. 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/dist/** 2 | **/node_modules/** 3 | **/server.js 4 | **/webpack.config*.js 5 | # TODO: figure out a way to lint this using flow instead of typescript 6 | examples/todos-flow 7 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'react-app', 3 | 4 | parser: '@typescript-eslint/parser', 5 | 6 | plugins: ['@typescript-eslint'], 7 | 8 | settings: { 9 | react: { 10 | version: '17' 11 | }, 12 | 'import/parsers': { 13 | '@typescript-eslint/parser': ['.ts', '.tsx'] 14 | }, 15 | 'import/resolver': { 16 | // use /tsconfig.json 17 | typescript: {} 18 | } 19 | }, 20 | 21 | rules: { 22 | 'jsx-a11y/href-no-hash': 'off', 23 | 'no-unused-vars': 'off', 24 | '@typescript-eslint/no-unused-expressions': 'off', 25 | '@typescript-eslint/no-unused-vars': [ 26 | 'error', 27 | { 28 | vars: 'all', 29 | args: 'after-used', 30 | ignoreRestSiblings: true, 31 | argsIgnorePattern: '^_', 32 | varsIgnorePattern: '^_' 33 | } 34 | ], 35 | '@typescript-eslint/consistent-type-imports': [ 36 | 'error', 37 | { prefer: 'type-imports', disallowTypeAnnotations: false } 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 6e53b1ee2301e68235f658f07d7bc85642ff597d 2 | 312a170df2467536e93362a052b77eb33adcb698 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | structure: 2 | readme: README.md 3 | summary: docs/README.md 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [timdorr, markerikson, phryneas, EskiMojo14] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41BBug report" 3 | about: Something is wrong with Redux. 4 | --- 5 | 6 | 24 | 25 | ### Prior Issues 26 | 27 | Are there any existing issues or PRs that relate to this problem? If so, link them here. 28 | 29 | ### What is the current behavior? 30 | 31 | What does Redux do right now. 32 | 33 | ### Steps to Reproduce 34 | 35 | If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://codesandbox.io or similar. 36 | 37 | ### What is the expected behavior? 38 | 39 | What should Redux be doing? 40 | 41 | ### Environment Details 42 | 43 | Which versions of Redux, and which browser and OS are affected by this issue? Did this work in previous versions of Redux? 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 👍 Feature Request 3 | about: I'd like Redux to do something new. 4 | --- 5 | 6 | 29 | 30 | ## New Features 31 | 32 | ### What is the new or updated feature that you are suggesting? 33 | 34 | ### Why should this feature be included? 35 | 36 | ### What docs changes are needed to explain this? 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🤔 Questions and Help 4 | url: https://redux.js.org/introduction/getting-started#help-and-discussion 5 | about: This is a bug tracker, not a support system. For usage questions, please use our support resources. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-edit.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4DD Documentation Fix" 3 | about: Fixing a problem in an existing docs page 4 | --- 5 | 6 | ## What docs page needs to be fixed? 7 | 8 | - **Section**: 9 | - **Page**: 10 | 11 | ## What is the problem? 12 | 13 | ## What should be changed to fix the problem? 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-new.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4D6 New/Updated Documentation Content" 3 | about: Adding a new docs page, or updating content in an existing docs page 4 | --- 5 | 6 | ## What docs page is being added or updated? 7 | 8 | - **Section**: 9 | - **Page**: 10 | 11 | ## For Adding New Content 12 | 13 | ### What kind of content category is this page (tutorial, how-to, explanation, reference)? 14 | 15 | ### Who is the intended target audience? 16 | 17 | #### What knowledge are we assuming they have? 18 | 19 | ### What are the intended results or takeaways from reading this page? 20 | 21 | ### What is the most critical info they should learn? 22 | 23 | ## For Updating Existing Content 24 | 25 | ### What updates should be made to the page? 26 | 27 | ### Do these updates change any of the assumptions or target audience? If so, how do they change? 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for the PR! 2 | 3 | To better assist you, please select the type of PR you want to create. 4 | 5 | Click the "Preview" tab above, and click on the link for the PR type: 6 | 7 | - [:bug: Bug fix or new feature](?template=bugfix.md) 8 | - [:memo: Documentation Fix](?template=documentation-edit.md) 9 | - [:book: New/Updated Documentation Content](?template=documentation-new.md) 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/bugfix.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: :bug: Bug fix or new feature 3 | about: Fixing a problem with Redux 4 | --- 5 | 6 | ## PR Type 7 | 8 | ### Does this PR add a new _feature_, or fix a _bug_? 9 | 10 | ### Why should this PR be included? 11 | 12 | ## Checklist 13 | 14 | - [ ] Have you added an explanation of what your changes do and why you'd like us to include them? 15 | - [ ] Is there an existing issue for this PR? 16 | - _link issue here_ 17 | - [ ] Have the files been linted and formatted? 18 | - [ ] Have the docs been updated to match the changes in the PR? 19 | - [ ] Have the tests been updated to match the changes in the PR? 20 | - [ ] Have you run the tests locally to confirm they pass? 21 | 22 | ## New Features 23 | 24 | ### What new capabilities does this PR add? 25 | 26 | ### What docs changes are needed to explain this? 27 | 28 | ## Bug Fixes 29 | 30 | ### What is the current behavior, and the steps to reproduce the issue? 31 | 32 | ### What is the expected behavior? 33 | 34 | ### How does this PR fix the problem? 35 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/documentation-edit.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: :memo: Documentation Fix 3 | about: Fixing a problem in an existing docs page 4 | --- 5 | 6 | ## Checklist 7 | 8 | - [ ] Is there an existing issue for this PR? 9 | - _link issue here_ 10 | - [ ] Have the files been linted and formatted? 11 | 12 | ## What docs page needs to be fixed? 13 | 14 | - **Section**: 15 | - **Page**: 16 | 17 | ## What is the problem? 18 | 19 | ## What changes does this PR make to fix the problem? 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/documentation-new.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: :book: New/Updated Documentation Content 3 | about: Adding a new docs page, or updating content in an existing docs page 4 | --- 5 | 6 | ## PR Type 7 | 8 | **Does this PR add a _new_ page, or update an _existing_ page?** 9 | 10 | ## Checklist 11 | 12 | - [ ] Is there an existing issue for this PR? 13 | - _link issue here_ 14 | - [ ] Have the files been linted and formatted? 15 | 16 | ## What docs page is being added or updated? 17 | 18 | - **Section**: 19 | - **Page**: 20 | 21 | ## For Adding New Content 22 | 23 | ### What kind of content category is this page (tutorial, how-to, explanation, reference)? 24 | 25 | ### Who is the intended target audience? 26 | 27 | #### What knowledge are we assuming they have? 28 | 29 | ### What are the intended results or takeaways from reading this page? 30 | 31 | ### What is the most critical info they should learn? 32 | 33 | ## For Updating Existing Content 34 | 35 | ### What updates should be made to the page? 36 | 37 | ### Do these updates change any of the assumptions or target audience? If so, how do they change? 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | # keeping it purely manual for now as to not accidentally trigger a release 4 | #release: 5 | # types: [published] 6 | workflow_dispatch: 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | id-token: write 12 | contents: read 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: '22.x' 18 | registry-url: 'https://registry.npmjs.org' 19 | cache: 'yarn' 20 | - run: yarn install --frozen-lockfile 21 | - run: yarn test 22 | - run: npm publish --access public --provenance 23 | env: 24 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 25 | -------------------------------------------------------------------------------- /.github/workflows/size.yaml: -------------------------------------------------------------------------------- 1 | name: Bundle Size 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Check compressed size 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 1 14 | - uses: preactjs/compressed-size-action@v2 15 | with: 16 | repo-token: '${{ secrets.GITHUB_TOKEN }}' 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | coverage 4 | 5 | dist 6 | lib 7 | es 8 | 9 | # Yarn 10 | .cache 11 | .yarnrc 12 | .yarn/cache/ 13 | .yarn/* 14 | !.yarn/patches 15 | !.yarn/releases 16 | !.yarn/plugins 17 | !.yarn/sdks 18 | !.yarn/versions 19 | .pnp.* 20 | *.tgz 21 | 22 | website/.yarn/ 23 | docs/.yarn/ 24 | 25 | # yalc 26 | .yalc 27 | yalc.lock 28 | 29 | website/translated_docs 30 | website/build/ 31 | website/node_modules 32 | website/i18n/* 33 | 34 | tsconfig.vitest-temp.json 35 | 36 | .vscode 37 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "none", 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "after:bump": "yarn && git add -u" 4 | }, 5 | "git": { 6 | "tagName": "v${version}" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | yarnPath: .yarn/releases/yarn-4.4.1.cjs 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | This project adheres to [Semantic Versioning](https://semver.org/). 4 | Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/reduxjs/redux/releases) page. 5 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | redux.js.org 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present Dan Abramov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PATRONS.md: -------------------------------------------------------------------------------- 1 | # Patrons 2 | 3 | The work on Redux was [funded by the community](https://www.patreon.com/reactdx). 4 | Meet some of the outstanding companies and individuals that made it possible: 5 | 6 | - [Webflow](https://github.com/webflow) 7 | - [Ximedes](https://www.ximedes.com/) 8 | - [HauteLook](https://hautelook.github.io/) 9 | - [Ken Wheeler](https://kenwheeler.github.io/) 10 | - [Chung Yen Li](https://www.facebook.com/prototocal.lee) 11 | - [Sunil Pai](https://twitter.com/threepointone) 12 | - [Charlie Cheever](https://twitter.com/ccheever) 13 | - [Eugene G](https://twitter.com/e1g) 14 | - [Matt Apperson](https://twitter.com/mattapperson) 15 | - [Jed Watson](https://twitter.com/jedwatson) 16 | - [Sasha Aickin](https://twitter.com/xander76) 17 | - [Stefan Tennigkeit](https://twitter.com/whobubble) 18 | - [Sam Vincent](https://twitter.com/samvincent) 19 | - Olegzandr Denman 20 | -------------------------------------------------------------------------------- /docs/api/utils.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: utils 3 | title: Additional Utilities 4 | hide_title: true 5 | description: 'API > utils: Additional utility functions' 6 | --- 7 | 8 |   9 | 10 | # Utility Functions 11 | 12 | The Redux core exports additional utility functions for reuse. 13 | 14 | ## `isAction` 15 | 16 | Returns true if the parameter is a valid Redux action object (a plain object with a string `type` field). 17 | 18 | This also serves as a TypeScript type predicate, which will narrow the TS type to `Action`. 19 | 20 | ## `isPlainObject` 21 | 22 | Returns true if the value appears to be a plain JS object. 23 | -------------------------------------------------------------------------------- /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/components/_FundamentalsWarning.mdx: -------------------------------------------------------------------------------- 1 | :::caution 2 | 3 | Note that **this tutorial intentionally shows older-style Redux logic patterns that require more code than the "modern Redux" patterns with Redux Toolkit we teach as the right approach for building apps with Redux today**, in order to explain the principles and concepts behind Redux. It's _not_ meant to be a production-ready project. 4 | 5 | See these pages to learn how to use "modern Redux" with Redux Toolkit: 6 | 7 | - [**The full "Redux Essentials" tutorial**](../tutorials/essentials/part-1-overview-concepts.md), which teaches "how to use Redux, the right way" with Redux Toolkit for real-world apps. **We recommend that all Redux learners should read the "Essentials" tutorial!** 8 | - [**Redux Fundamentals, Part 8: Modern Redux with Redux Toolkit**](../tutorials/fundamentals/part-8-modern-redux.md), which shows how to convert the low-level examples from earlier sections into modern Redux Toolkit equivalents 9 | 10 | ::: 11 | -------------------------------------------------------------------------------- /docs/introduction/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | - [Core Concepts](CoreConcepts.md) 4 | - [Learning Resources](LearningResources.md) 5 | - [Ecosystem](Ecosystem.md) 6 | - [Examples](Examples.md) 7 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "devDependencies": { 4 | "@reduxjs/toolkit": "^2.0.1", 5 | "@testing-library/react": "^14.1.2", 6 | "msw": "^2.0.0", 7 | "react": "^18.2.0", 8 | "react-redux": "^9.1.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/usage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: index 3 | title: Usage Guides Index 4 | sidebar_label: Usage Guides Index 5 | --- 6 | 7 | # Usage Guides 8 | 9 | The Usage Guides section provides practical guidance on how to correctly use Redux in real-world applications, including project setup and architecture, patterns, practices, and techniques. 10 | 11 | :::info Prerequisites 12 | 13 | The pages in this category assume you understand the core Redux terms and concepts explained in [the "Redux Fundamentals" tutorial](../tutorials/fundamentals/part-1-overview.md), including actions, reducers, stores, immutability, React-Redux, and async logic. 14 | 15 | ::: 16 | 17 | ## Setup and Organization 18 | 19 | This section covers information on how to set up and organize Redux-based projects. 20 | 21 | - [Configuring Your Store](ConfiguringYourStore.md) 22 | - [Code Splitting](CodeSplitting.md) 23 | - [Server Rendering](ServerRendering.md) 24 | - [Isolating Redux Sub-Apps](IsolatingSubapps.md) 25 | 26 | ## Code Quality 27 | 28 | This section provides information on tools and techniques used to improve the quality of your Redux code. 29 | 30 | - [Usage with TypeScript](UsageWithTypescript.md) 31 | - [Writing Tests](WritingTests.mdx) 32 | - [Troubleshooting](Troubleshooting.md) 33 | 34 | ## Redux Logic and Patterns 35 | 36 | This section provides information about typical Redux patterns and approaches for writing different kinds of Redux logic. 37 | 38 | - [Structuring Reducers](structuring-reducers/StructuringReducers.md) 39 | - [Reducing Boilerplate](ReducingBoilerplate.md) 40 | - [Deriving Data with Selectors](../usage/deriving-data-selectors.md) 41 | - [Implementing Undo History](ImplementingUndoHistory.md) 42 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Read the descriptions for every example on the [Examples](../docs/introduction/Examples.md) documentation page. 4 | -------------------------------------------------------------------------------- /examples/async/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | build 8 | 9 | # misc 10 | .DS_Store 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /examples/async/README.md: -------------------------------------------------------------------------------- 1 | # Redux Async Example 2 | 3 | > **Warning**: This example is outdated, and shows legacy patterns that we no longer teach or recommend. 4 | > Please see the Redux docs tutorials for our recommended usage patterns, and specifically the "Essentials" tutorial: 5 | > **https://redux.js.org/tutorials/index** 6 | -------------------------------------------------------------------------------- /examples/async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "^4.0.3" 7 | }, 8 | "dependencies": { 9 | "prop-types": "^15.7.2", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-redux": "^7.2.0", 13 | "redux": "^4.0.5", 14 | "redux-logger": "^3.0.6", 15 | "redux-thunk": "^2.3.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject", 21 | "test": "react-scripts test --env=node --passWithNoTests" 22 | }, 23 | "browserslist": [ 24 | ">0.2%", 25 | "not dead", 26 | "not ie <= 11", 27 | "not op_mini all" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /examples/async/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Redux Async Example 7 | 8 | 9 |
10 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/async/src/actions/index.js: -------------------------------------------------------------------------------- 1 | export const REQUEST_POSTS = 'REQUEST_POSTS' 2 | export const RECEIVE_POSTS = 'RECEIVE_POSTS' 3 | export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT' 4 | export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT' 5 | 6 | export const selectSubreddit = subreddit => ({ 7 | type: SELECT_SUBREDDIT, 8 | subreddit 9 | }) 10 | 11 | export const invalidateSubreddit = subreddit => ({ 12 | type: INVALIDATE_SUBREDDIT, 13 | subreddit 14 | }) 15 | 16 | export const requestPosts = subreddit => ({ 17 | type: REQUEST_POSTS, 18 | subreddit 19 | }) 20 | 21 | export const receivePosts = (subreddit, json) => ({ 22 | type: RECEIVE_POSTS, 23 | subreddit, 24 | posts: json.data.children.map(child => child.data), 25 | receivedAt: Date.now() 26 | }) 27 | 28 | const fetchPosts = subreddit => dispatch => { 29 | dispatch(requestPosts(subreddit)) 30 | return fetch(`https://www.reddit.com/r/${subreddit}.json`) 31 | .then(response => response.json()) 32 | .then(json => dispatch(receivePosts(subreddit, json))) 33 | } 34 | 35 | const shouldFetchPosts = (state, subreddit) => { 36 | const posts = state.postsBySubreddit[subreddit] 37 | if (!posts) { 38 | return true 39 | } 40 | if (posts.isFetching) { 41 | return false 42 | } 43 | return posts.didInvalidate 44 | } 45 | 46 | export const fetchPostsIfNeeded = subreddit => (dispatch, getState) => { 47 | if (shouldFetchPosts(getState(), subreddit)) { 48 | return dispatch(fetchPosts(subreddit)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/async/src/components/Picker.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Picker = ({ value, onChange, options }) => ( 5 | 6 |

{value}

7 | 14 |
15 | ) 16 | 17 | Picker.propTypes = { 18 | options: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, 19 | value: PropTypes.string.isRequired, 20 | onChange: PropTypes.func.isRequired 21 | } 22 | 23 | export default Picker 24 | -------------------------------------------------------------------------------- /examples/async/src/components/Posts.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const Posts = ({ posts }) => ( 5 | 10 | ) 11 | 12 | Posts.propTypes = { 13 | posts: PropTypes.array.isRequired 14 | } 15 | 16 | export default Posts 17 | -------------------------------------------------------------------------------- /examples/async/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { createStore, applyMiddleware } from 'redux' 4 | import { Provider } from 'react-redux' 5 | import { thunk } from 'redux-thunk' 6 | import { createLogger } from 'redux-logger' 7 | import reducer from './reducers' 8 | import App from './containers/App' 9 | 10 | const middleware = [thunk] 11 | if (process.env.NODE_ENV !== 'production') { 12 | middleware.push(createLogger()) 13 | } 14 | 15 | const store = createStore(reducer, applyMiddleware(...middleware)) 16 | 17 | render( 18 | 19 | 20 | , 21 | document.getElementById('root') 22 | ) 23 | -------------------------------------------------------------------------------- /examples/async/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import { 3 | SELECT_SUBREDDIT, 4 | INVALIDATE_SUBREDDIT, 5 | REQUEST_POSTS, 6 | RECEIVE_POSTS 7 | } from '../actions' 8 | 9 | const selectedSubreddit = (state = 'reactjs', action) => { 10 | switch (action.type) { 11 | case SELECT_SUBREDDIT: 12 | return action.subreddit 13 | default: 14 | return state 15 | } 16 | } 17 | 18 | const posts = ( 19 | state = { 20 | isFetching: false, 21 | didInvalidate: false, 22 | items: [] 23 | }, 24 | action 25 | ) => { 26 | switch (action.type) { 27 | case INVALIDATE_SUBREDDIT: 28 | return { 29 | ...state, 30 | didInvalidate: true 31 | } 32 | case REQUEST_POSTS: 33 | return { 34 | ...state, 35 | isFetching: true, 36 | didInvalidate: false 37 | } 38 | case RECEIVE_POSTS: 39 | return { 40 | ...state, 41 | isFetching: false, 42 | didInvalidate: false, 43 | items: action.posts, 44 | lastUpdated: action.receivedAt 45 | } 46 | default: 47 | return state 48 | } 49 | } 50 | 51 | const postsBySubreddit = (state = {}, action) => { 52 | switch (action.type) { 53 | case INVALIDATE_SUBREDDIT: 54 | case RECEIVE_POSTS: 55 | case REQUEST_POSTS: 56 | return { 57 | ...state, 58 | [action.subreddit]: posts(state[action.subreddit], action) 59 | } 60 | default: 61 | return state 62 | } 63 | } 64 | 65 | const rootReducer = combineReducers({ 66 | postsBySubreddit, 67 | selectedSubreddit 68 | }) 69 | 70 | export default rootReducer 71 | -------------------------------------------------------------------------------- /examples/counter-ts/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/counter-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "counter-ts", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^2.1.0", 7 | "react": "^18.2.0", 8 | "react-dom": "^18.2.0", 9 | "react-redux": "^9.1.0" 10 | }, 11 | "devDependencies": { 12 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 13 | "@testing-library/jest-dom": "^6.4.0", 14 | "@testing-library/react": "^14.1.2", 15 | "@testing-library/user-event": "^14.5.2", 16 | "@types/jest": "^29.5.11", 17 | "@types/node": "^20.11.10", 18 | "@types/react": "^18.2.48", 19 | "@types/react-dom": "^18.2.18", 20 | "react-scripts": "5.0.1", 21 | "typescript": "^5.3.3", 22 | "web-vitals": "^3.5.2" 23 | }, 24 | "overrides": { 25 | "typescript": "^5.3.3" 26 | }, 27 | "scripts": { 28 | "start": "react-scripts start", 29 | "build": "react-scripts build", 30 | "test": "react-scripts test", 31 | "eject": "react-scripts eject" 32 | }, 33 | "eslintConfig": { 34 | "extends": [ 35 | "react-app", 36 | "react-app/jest" 37 | ] 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/counter-ts/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/redux/f2495915e7fc6e1e235cb7f35d83ddd7f61803e2/examples/counter-ts/public/favicon.ico -------------------------------------------------------------------------------- /examples/counter-ts/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/counter-ts/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/redux/f2495915e7fc6e1e235cb7f35d83ddd7f61803e2/examples/counter-ts/public/logo192.png -------------------------------------------------------------------------------- /examples/counter-ts/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reduxjs/redux/f2495915e7fc6e1e235cb7f35d83ddd7f61803e2/examples/counter-ts/public/logo512.png -------------------------------------------------------------------------------- /examples/counter-ts/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/counter-ts/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/counter-ts/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-float infinite 3s ease-in-out; 13 | } 14 | } 15 | 16 | .App-header { 17 | min-height: 100vh; 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | justify-content: center; 22 | font-size: calc(10px + 2vmin); 23 | } 24 | 25 | .App-link { 26 | color: rgb(112, 76, 182); 27 | } 28 | 29 | @keyframes App-logo-float { 30 | 0% { 31 | transform: translateY(0); 32 | } 33 | 50% { 34 | transform: translateY(10px); 35 | } 36 | 100% { 37 | transform: translateY(0px); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/counter-ts/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import { Provider } from 'react-redux' 4 | import { store } from './app/store' 5 | import App from './App' 6 | 7 | test('renders learn react link', () => { 8 | const { getByText } = render( 9 | 10 | 11 | 12 | ) 13 | 14 | expect(getByText(/learn/i)).toBeInTheDocument() 15 | }) 16 | -------------------------------------------------------------------------------- /examples/counter-ts/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Counter } from './features/counter/Counter' 3 | import './App.css' 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | logo 14 | 15 |

16 | Edit src/App.tsx and save to reload. 17 |

18 | 19 | Learn 20 | 26 | React 27 | 28 | , 29 | 35 | Redux 36 | 37 | , 38 | 44 | Redux Toolkit 45 | 46 | , and 47 | 53 | React Redux 54 | 55 | 56 |
57 |
58 | ) 59 | } 60 | 61 | export default App 62 | -------------------------------------------------------------------------------- /examples/counter-ts/src/app/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import type { AppDispatch, RootState } from './store' 3 | 4 | // Use throughout your app instead of plain `useDispatch` and `useSelector` 5 | export const useAppDispatch = useDispatch.withTypes() 6 | export const useAppSelector = useSelector.withTypes() 7 | -------------------------------------------------------------------------------- /examples/counter-ts/src/app/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit' 2 | import counterReducer from '../features/counter/counterSlice' 3 | 4 | export const store = configureStore({ 5 | reducer: { 6 | counter: counterReducer 7 | } 8 | }) 9 | 10 | export type AppDispatch = typeof store.dispatch 11 | export type RootState = ReturnType 12 | export type AppThunk = ThunkAction< 13 | ReturnType, 14 | RootState, 15 | unknown, 16 | Action 17 | > 18 | -------------------------------------------------------------------------------- /examples/counter-ts/src/features/counter/Counter.module.css: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | } 6 | 7 | .row > button { 8 | margin-left: 4px; 9 | margin-right: 8px; 10 | } 11 | 12 | .row:not(:last-child) { 13 | margin-bottom: 16px; 14 | } 15 | 16 | .value { 17 | font-size: 78px; 18 | padding-left: 16px; 19 | padding-right: 16px; 20 | margin-top: 2px; 21 | font-family: 'Courier New', Courier, monospace; 22 | } 23 | 24 | .button { 25 | appearance: none; 26 | background: none; 27 | font-size: 32px; 28 | padding-left: 12px; 29 | padding-right: 12px; 30 | outline: none; 31 | border: 2px solid transparent; 32 | color: rgb(112, 76, 182); 33 | padding-bottom: 4px; 34 | cursor: pointer; 35 | background-color: rgba(112, 76, 182, 0.1); 36 | border-radius: 2px; 37 | transition: all 0.15s; 38 | } 39 | 40 | .textbox { 41 | font-size: 32px; 42 | padding: 2px; 43 | width: 64px; 44 | text-align: center; 45 | margin-right: 4px; 46 | } 47 | 48 | .button:hover, 49 | .button:focus { 50 | border: 2px solid rgba(112, 76, 182, 0.4); 51 | } 52 | 53 | .button:active { 54 | background-color: rgba(112, 76, 182, 0.2); 55 | } 56 | 57 | .asyncButton { 58 | composes: button; 59 | position: relative; 60 | } 61 | 62 | .asyncButton:after { 63 | content: ''; 64 | background-color: rgba(112, 76, 182, 0.15); 65 | display: block; 66 | position: absolute; 67 | width: 100%; 68 | height: 100%; 69 | left: 0; 70 | top: 0; 71 | opacity: 0; 72 | transition: 73 | width 1s linear, 74 | opacity 0.5s ease 1s; 75 | } 76 | 77 | .asyncButton:active:after { 78 | width: 0%; 79 | opacity: 1; 80 | transition: 0s; 81 | } 82 | -------------------------------------------------------------------------------- /examples/counter-ts/src/features/counter/counterAPI.ts: -------------------------------------------------------------------------------- 1 | // A mock function to mimic making an async request for data 2 | export function fetchCount(amount = 1) { 3 | return new Promise<{ data: number }>(resolve => 4 | setTimeout(() => resolve({ data: amount }), 500) 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /examples/counter-ts/src/features/counter/counterSlice.spec.ts: -------------------------------------------------------------------------------- 1 | import counterReducer, { 2 | CounterState, 3 | increment, 4 | decrement, 5 | incrementByAmount 6 | } from './counterSlice' 7 | 8 | describe('counter reducer', () => { 9 | const initialState: CounterState = { 10 | value: 3, 11 | status: 'idle' 12 | } 13 | it('should handle initial state', () => { 14 | expect(counterReducer(undefined, { type: 'unknown' })).toEqual({ 15 | value: 0, 16 | status: 'idle' 17 | }) 18 | }) 19 | 20 | it('should handle increment', () => { 21 | const actual = counterReducer(initialState, increment()) 22 | expect(actual.value).toEqual(4) 23 | }) 24 | 25 | it('should handle decrement', () => { 26 | const actual = counterReducer(initialState, decrement()) 27 | expect(actual.value).toEqual(2) 28 | }) 29 | 30 | it('should handle incrementByAmount', () => { 31 | const actual = counterReducer(initialState, incrementByAmount(2)) 32 | expect(actual.value).toEqual(5) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /examples/counter-ts/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/counter-ts/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { Provider } from 'react-redux' 4 | import { store } from './app/store' 5 | import App from './App' 6 | import reportWebVitals from './reportWebVitals' 7 | import './index.css' 8 | 9 | const container = document.getElementById('root')! 10 | const root = createRoot(container) 11 | 12 | root.render( 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals() 24 | -------------------------------------------------------------------------------- /examples/counter-ts/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/counter-ts/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals' 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry) 7 | getFID(onPerfEntry) 8 | getFCP(onPerfEntry) 9 | getLCP(onPerfEntry) 10 | getTTFB(onPerfEntry) 11 | }) 12 | } 13 | } 14 | 15 | export default reportWebVitals 16 | -------------------------------------------------------------------------------- /examples/counter-ts/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom' 6 | -------------------------------------------------------------------------------- /examples/counter-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/counter-vanilla/README.md: -------------------------------------------------------------------------------- 1 | # Redux Counter Vanilla Example 2 | 3 | This example does not require a build system or a view framework, and exists to show the raw Redux API used with ES5. 4 | 5 | As Dan said [in the original PR for this example](): 6 | 7 | > The new Counter Vanilla example is aimed to dispel the myth that Redux requires Webpack, React, hot reloading, sagas, action creators, constants, Babel, npm, CSS modules, decorators, fluent Latin, an Egghead subscription, a PhD, or an Exceeds Expectations O.W.L. level. 8 | > 9 | > Nope, it's just HTML, some artisanal `