diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 67de875d37..55421debe5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ This repo uses Yarn 3 for all package management. Please ensure that Yarn 1.x is #### Building Redux -Running the `build` task will create a CommonJS module-per-module build, a ES Modules build and a UMD build. +Running the `build` task will build the artifacts into the `./dist` folder ```sh yarn build diff --git a/README.md b/README.md index b79c3e657b..b4d80cb5a0 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,39 @@ # Redux Logo Redux is a predictable state container for JavaScript apps. -(Not to be confused with a WordPress framework – [Redux Framework](https://redux.io)) It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as [live code editing combined with a time traveling debugger](https://github.com/reduxjs/redux-devtools). -You can use Redux together with [React](https://reactjs.org), or with any other view library. -It is tiny (2kB, including dependencies), and has a rich ecosystem of addons. +You can use Redux together with [React](https://react.dev), or with any other view library. The Redux core is tiny (2kB, including dependencies), and has a rich ecosystem of addons. + +[**Redux Toolkit**](https://redux-toolkit.js.org) is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications. ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux/test.yaml?branch=master&event=push&style=flat-square) [![npm version](https://img.shields.io/npm/v/redux.svg?style=flat-square)](https://www.npmjs.com/package/redux) [![npm downloads](https://img.shields.io/npm/dm/redux.svg?style=flat-square)](https://www.npmjs.com/package/redux) [![redux channel on discord](https://img.shields.io/badge/discord-%23redux%20%40%20reactiflux-61dafb.svg?style=flat-square)](https://discord.gg/0ZcbPKXt5bZ6au5t) -[![Changelog #187](https://img.shields.io/badge/changelog-%23187-lightgrey.svg?style=flat-square)](https://changelog.com/187) ## Installation -[**Redux Toolkit**](https://redux-toolkit.js.org) is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications. +### Create a React Redux App + +The recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux). + +Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features. + +```bash +# Vite with our Redux+TS template +# (using the `degit` tool to clone and extract the template) +npx degit reduxjs/redux-templates/packages/vite-template-redux my-app + +# Next.js using the `with-redux` template +npx create-next-app --example with-redux my-app +``` + +We do not currently have official React Native templates, but recommend these templates for standard React Native and for Expo: + +- https://github.com/rahsheen/react-native-template-redux-typescript +- https://github.com/rahsheen/expo-template-redux-typescript ``` npm install @reduxjs/toolkit react-redux @@ -32,7 +49,7 @@ For more details, see [the Installation docs page](https://redux.js.org/introduc ## Documentation -The Redux docs are located at **https://redux.js.org**: +The Redux core docs are located at **https://redux.js.org**, and include the full Redux tutorials, as well usage guides on general Redux patterns: - [Introduction](https://redux.js.org/introduction/getting-started) - [Tutorials](https://redux.js.org/tutorials/index) @@ -40,6 +57,8 @@ The Redux docs are located at **https://redux.js.org**: - [FAQ](https://redux.js.org/faq) - [API Reference](https://redux.js.org/api/api-reference) +The Redux Toolkit docs are available at **https://redux-toolkit.js.org**, including API references and usage guides for all of the APIs included in Redux Toolkit. + ## Learn Redux ### Redux Essentials Tutorial @@ -50,20 +69,6 @@ The [**Redux Essentials tutorial**](https://redux.js.org/tutorials/essentials/pa The [**Redux Fundamentals tutorial**](https://redux.js.org/tutorials/fundamentals/part-1-overview) is a "bottom-up" tutorial that teaches "how Redux works" from first principles and without any abstractions, and why standard Redux usage patterns exist. -### Additional Tutorials - -- The Redux repository contains several example projects demonstrating various aspects of how to use Redux. Almost all examples have a corresponding CodeSandbox sandbox. This is an interactive version of the code that you can play with online. See the complete list of examples in the **[Examples page](https://redux.js.org/introduction/examples)**. -- Redux creator Dan Abramov's **free ["Getting Started with Redux" video series](https://app.egghead.io/playlists/fundamentals-of-redux-course-from-dan-abramov-bd5cc867)** and **[Building React Applications with Idiomatic Redux](https://egghead.io/courses/building-react-applications-with-idiomatic-redux)** video courses on Egghead.io -- Redux maintainer Mark Erikson's **["Redux Fundamentals" conference talk](https://blog.isquaredsoftware.com/2018/03/presentation-reactathon-redux-fundamentals/)** and [**"Redux Fundamentals" workshop slides**](https://blog.isquaredsoftware.com/2018/06/redux-fundamentals-workshop-slides/) -- Dave Ceddia's post [**A Complete React Redux Tutorial for Beginners**](https://daveceddia.com/redux-tutorial/) - -### Other Resources - -- The **[Redux FAQ](https://redux.js.org/faq)** answers many common questions about how to use Redux, and the **["Using Redux" docs section](https://redux.js.org/usage/index)** has information on handling derived data, testing, structuring reducer logic, and reducing boilerplate. -- Redux maintainer Mark Erikson's **["Practical Redux" tutorial series](https://blog.isquaredsoftware.com/series/practical-redux/)** demonstrates real-world intermediate and advanced techniques for working with React and Redux (also available as **[an interactive course on Educative.io](https://www.educative.io/collection/5687753853370368/5707702298738688)**). -- The **[React/Redux links list](https://github.com/markerikson/react-redux-links)** has categorized articles on working with [reducers and selectors](https://github.com/markerikson/react-redux-links/blob/master/redux-reducers-selectors.md), [managing side effects](https://github.com/markerikson/react-redux-links/blob/master/redux-side-effects.md), [Redux architecture and best practices](https://github.com/markerikson/react-redux-links/blob/master/redux-architecture.md), and more. -- Our community has created thousands of Redux-related libraries, addons, and tools. The **["Ecosystem" docs page](https://redux.js.org/introduction/ecosystem)** lists our recommendations, and also there's a complete listing available in the **[Redux addons catalog](https://github.com/markerikson/redux-ecosystem-links)**. - ### Help and Discussion The **[#redux channel](https://discord.gg/0ZcbPKXt5bZ6au5t)** of the **[Reactiflux Discord community](https://www.reactiflux.com)** is our official resource for all questions related to learning and using Redux. Reactiflux is a great place to hang out, ask questions, and learn - please come and join us there! @@ -88,76 +93,13 @@ Yes, these guidelines are subjective and vague, but this is for a good reason. T > - **[The Tao of Redux, Part 2 - Practice and Philosophy](https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-2/)** > - **[Redux FAQ](https://redux.js.org/faq)** -## Developer Experience - -Dan Abramov (author of Redux) wrote Redux while working on his React Europe talk called [“Hot Reloading with Time Travel”](https://www.youtube.com/watch?v=xsSnOQynTHs). His goal was to create a state management library with a minimal API but completely predictable behavior. Redux makes it possible to implement logging, hot reloading, time travel, universal apps, record and replay, without any buy-in from the developer. - -## Influences - -Redux evolves the ideas of [Flux](https://facebook.github.io/flux/), but avoids its complexity by taking cues from [Elm](https://github.com/evancz/elm-architecture-tutorial/). -Even if you haven't used Flux or Elm, Redux only takes a few minutes to get started with. - ## Basic Example The whole global state of your app is stored in an object tree inside a single _store_. The only way to change the state tree is to create an _action_, an object describing what happened, and _dispatch_ it to the store. To specify how state gets updated in response to an action, you write pure _reducer_ functions that calculate a new state based on the old state and the action. -```js -import { createStore } from 'redux' - -/** - * This is a reducer - a function that takes a current state value and an - * action object describing "what happened", and returns a new state value. - * A reducer's function signature is: (state, action) => newState - * - * The Redux state should contain only plain JS objects, arrays, and primitives. - * The root state value is usually an object. It's important that you should - * not mutate the state object, but return a new object if the state changes. - * - * You can use any conditional logic you want in a reducer. In this example, - * we use a switch statement, but it's not required. - */ -function counterReducer(state = { value: 0 }, action) { - switch (action.type) { - case 'counter/incremented': - return { value: state.value + 1 } - case 'counter/decremented': - return { value: state.value - 1 } - default: - return state - } -} - -// Create a Redux store holding the state of your app. -// Its API is { subscribe, dispatch, getState }. -let store = createStore(counterReducer) - -// You can use subscribe() to update the UI in response to state changes. -// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly. -// There may be additional use cases where it's helpful to subscribe as well. - -store.subscribe(() => console.log(store.getState())) - -// The only way to mutate the internal state is to dispatch an action. -// The actions can be serialized, logged or stored and later replayed. -store.dispatch({ type: 'counter/incremented' }) -// {value: 1} -store.dispatch({ type: 'counter/incremented' }) -// {value: 2} -store.dispatch({ type: 'counter/decremented' }) -// {value: 1} -``` - -Instead of mutating the state directly, you specify the mutations you want to happen with plain objects called _actions_. Then you write a special function called a _reducer_ to decide how every action transforms the entire application's state. - -In a typical Redux app, there is just a single store with a single root reducing function. As your app grows, you split the root reducer into smaller reducers independently operating on the different parts of the state tree. This is exactly like how there is just one root component in a React app, but it is composed out of many small components. - -This architecture might seem like a lot for a counter app, but the beauty of this pattern is how well it scales to large and complex apps. It also enables very powerful developer tools, because it is possible to trace every mutation to the action that caused it. You can record user sessions and reproduce them just by replaying every action. - -### Redux Toolkit Example - -Redux Toolkit simplifies the process of writing Redux logic and setting up the store. With Redux Toolkit, that same logic looks like: +Redux Toolkit simplifies the process of writing Redux logic and setting up the store. With Redux Toolkit, the basic app logic looks like: ```js import { createSlice, configureStore } from '@reduxjs/toolkit' @@ -199,48 +141,7 @@ store.dispatch(decremented()) // {value: 1} ``` -Redux Toolkit allows us to write shorter logic that's easier to read, while still following the same Redux behavior and data flow. - -## Examples - -Almost all examples have a corresponding CodeSandbox sandbox. This is an interactive version of the code that you can play with online. - -- [**Counter Vanilla**](https://redux.js.org/introduction/examples#counter-vanilla): [Source](https://github.com/reduxjs/redux/tree/master/examples/counter-vanilla) -- [**Counter**](https://redux.js.org/introduction/examples#counter): [Source](https://github.com/reduxjs/redux/tree/master/examples/counter) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/counter) -- [**Todos**](https://redux.js.org/introduction/examples#todos): [Source](https://github.com/reduxjs/redux/tree/master/examples/todos) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/todos) -- [**Todos with Undo**](https://redux.js.org/introduction/examples#todos-with-undo): [Source](https://github.com/reduxjs/redux/tree/master/examples/todos-with-undo) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/todos-with-undo) -- [**TodoMVC**](https://redux.js.org/introduction/examples#todomvc): [Source](https://github.com/reduxjs/redux/tree/master/examples/todomvc) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/todomvc) -- [**Shopping Cart**](https://redux.js.org/introduction/examples#shopping-cart): [Source](https://github.com/reduxjs/redux/tree/master/examples/shopping-cart) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/shopping-cart) -- [**Tree View**](https://redux.js.org/introduction/examples#tree-view): [Source](https://github.com/reduxjs/redux/tree/master/examples/tree-view) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/tree-view) -- [**Async**](https://redux.js.org/introduction/examples#async): [Source](https://github.com/reduxjs/redux/tree/master/examples/async) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/async) -- [**Universal**](https://redux.js.org/introduction/examples#universal): [Source](https://github.com/reduxjs/redux/tree/master/examples/universal) -- [**Real World**](https://redux.js.org/introduction/examples#real-world): [Source](https://github.com/reduxjs/redux/tree/master/examples/real-world) | [Sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/real-world) - -## Testimonials - -> [“Love what you're doing with Redux”](https://twitter.com/jingc/status/616608251463909376) -> Jing Chen, creator of Flux - -> [“I asked for comments on Redux in FB's internal JS discussion group, and it was universally praised. Really awesome work.”](https://twitter.com/fisherwebdev/status/616286955693682688) -> Bill Fisher, author of Flux documentation - -> [“It's cool that you are inventing a better Flux by not doing Flux at all.”](https://twitter.com/andrestaltz/status/616271392930201604) -> André Staltz, creator of Cycle - -## Thanks - -- [The Elm Architecture](https://github.com/evancz/elm-architecture-tutorial) for a great intro to modeling state updates with reducers; -- [Turning the database inside-out](https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/) for blowing my mind; -- [Developing ClojureScript with Figwheel](https://www.youtube.com/watch?v=j-kj2qwJa_E) for convincing me that re-evaluation should “just work”; -- [Webpack](https://webpack.js.org/concepts/hot-module-replacement/) for Hot Module Replacement; -- [Flummox](https://github.com/acdlite/flummox) for teaching me to approach Flux without boilerplate or singletons; -- [disto](https://github.com/threepointone/disto) for a proof of concept of hot reloadable Stores; -- [NuclearJS](https://github.com/optimizely/nuclear-js) for proving this architecture can be performant; -- [Om](https://github.com/omcljs/om) for popularizing the idea of a single state atom; -- [Cycle](https://github.com/cyclejs/cycle-core) for showing how often a function is the best tool; -- [React](https://github.com/facebook/react) for the pragmatic innovation. - -Special thanks to [Jamie Paton](https://jdpaton.github.io) for handing over the `redux` NPM package name. +Redux Toolkit allows us to write shorter logic that's easier to read, while still following the original core Redux behavior and data flow. ## Logo @@ -251,16 +152,6 @@ You can find the official logo [on GitHub](https://github.com/reduxjs/redux/tree This project adheres to [Semantic Versioning](https://semver.org/). Every release, along with the migration instructions, is documented on the GitHub [Releases](https://github.com/reduxjs/redux/releases) page. -## Patrons - -The work on Redux was [funded by the community](https://www.patreon.com/reactdx). -Meet some of the outstanding companies that made it possible: - -- [Webflow](https://github.com/webflow) -- [Ximedes](https://www.ximedes.com/) - -[See the full list of Redux patrons](PATRONS.md), as well as the always-growing list of [people and companies that use Redux](https://github.com/reduxjs/redux/issues/310). - ## License [MIT](LICENSE.md) diff --git a/docs/api/Store.md b/docs/api/Store.md index 31b077ff4d..0554dc2be9 100644 --- a/docs/api/Store.md +++ b/docs/api/Store.md @@ -7,21 +7,11 @@ description: 'API > Store: the core Redux store methods' # Store A store holds the whole [state tree](../understanding/thinking-in-redux/Glossary.md#state) of your application. -The only way to change the state inside it is to dispatch an [action](../understanding/thinking-in-redux/Glossary.md#action) on it. +The only way to change the state inside it is to dispatch an [action](../understanding/thinking-in-redux/Glossary.md#action) on it, which triggers the [root reducer function](../understanding/thinking-in-redux/Glossary.md#reducer) to calculate the new state. A store is not a class. It's just an object with a few methods on it. -To create it, pass your root [reducing function](../understanding/thinking-in-redux/Glossary.md#reducer) to [`createStore`](createStore.md). -> ##### A Note for Flux Users -> -> If you're coming from Flux, there is a single important difference you need to understand. Redux doesn't have a Dispatcher or support many stores. **Instead, there is just a single store with a single root [reducing function](../understanding/thinking-in-redux/Glossary.md#reducer).** As your app grows, instead of adding stores, you split the root reducer into smaller reducers independently operating on the different parts of the state tree. You can use a helper like [`combineReducers`](combineReducers.md) to combine them. This is similar to how there is just one root component in a React app, but it is composed out of many small components. - -### Store Methods - -- [`getState()`](#getstate) -- [`dispatch(action)`](#dispatchaction) -- [`subscribe(listener)`](#subscribelistener) -- [`replaceReducer(nextReducer)`](#replacereducernextreducer) +To create a store, **pass your root [reducer function](../understanding/thinking-in-redux/Glossary.md#reducer) to Redux Toolkit's [`configureStore` method](https://redux-toolkit.js.org/api/configureStore)**, which will set up a Redux store with a good default configuration. (Alternately, if you're not yet using Redux Toolkit, you can use the original [`createStore`](createStore.md) method, but we encourage you to [migrate your code to use Redux Toolkit](../usage/migrating-to-modern-redux.mdx) as soon as possible) ## Store Methods @@ -42,13 +32,15 @@ _(any)_: The current state tree of your application. Dispatches an action. This is the only way to trigger a state change. -The store's reducing function will be called with the current [`getState()`](#getState) result and the given `action` synchronously. Its return value will be considered the next state. It will be returned from [`getState()`](#getState) from now on, and the change listeners will immediately be notified. +The store's reducer function will be called with the current [`getState()`](#getState) result and the given `action` synchronously. Its return value will be considered the next state. It will be returned from [`getState()`](#getState) from now on, and the change listeners will immediately be notified. + +:::caution + +If you attempt to call `dispatch` from inside the [reducer](../understanding/thinking-in-redux/Glossary.md#reducer), it will throw with an error saying "Reducers may not dispatch actions." Reducers are pure functions - they can _only_ return a new state value and must not have side effects (and dispatching is a side effect). + +In Redux, subscriptions are called after the root reducer has returned the new state, so you _may_ dispatch in the subscription listeners. You are only disallowed to dispatch inside the reducers because they must have no side effects. If you want to cause a side effect in response to an action, the right place to do this is in the potentially async [action creator](../understanding/thinking-in-redux/Glossary.md#action-creator). -> ##### A Note for Flux Users -> -> If you attempt to call `dispatch` from inside the [reducer](../understanding/thinking-in-redux/Glossary.md#reducer), it will throw with an error saying “Reducers may not dispatch actions.” This is similar to “Cannot dispatch in a middle of dispatch” error in Flux, but doesn't cause the problems associated with it. In Flux, a dispatch is forbidden while Stores are handling the action and emitting updates. This is unfortunate because it makes it impossible to dispatch actions from component lifecycle hooks or other benign places. -> -> In Redux, subscriptions are called after the root reducer has returned the new state, so you _may_ dispatch in the subscription listeners. You are only disallowed to dispatch inside the reducers because they must have no side effects. If you want to cause a side effect in response to an action, the right place to do this is in the potentially async [action creator](../understanding/thinking-in-redux/Glossary.md#action-creator). +::: #### Arguments diff --git a/docs/api/api-reference.md b/docs/api/api-reference.md index 8e554ba58c..43f4bb2dd3 100644 --- a/docs/api/api-reference.md +++ b/docs/api/api-reference.md @@ -5,44 +5,41 @@ title: API Reference # API Reference -The Redux API surface is tiny. Redux defines a set of contracts for you to implement (such as [reducers](../understanding/thinking-in-redux/Glossary.md#reducer)) and provides a few helper functions to tie these contracts together. +This section documents the original Redux core API. The Redux core is small - it defines a set of contracts for you to implement (such as [reducers](../understanding/thinking-in-redux/Glossary.md#reducer)) and provides a few helper functions to tie these contracts together. -This section documents the complete Redux API. Keep in mind that Redux is only concerned with managing the state. In a real app, you'll also want to use UI bindings like [react-redux](https://github.com/gaearon/react-redux). +**In practice, you won't use the Redux core directly**. [**Redux Toolkit**](https://redux-toolkit.js.org) is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications. Additionally, [**React-Redux**](https://react-redux.js.org) lets your React components talk to the Redux store. -### Top-Level Exports +See their API docs here: -- [createStore(reducer, [preloadedState], [enhancer])](createStore.md) +- https://redux-toolkit.js.org/ +- https://react-redux.js.org/ + +:::danger + +**The original Redux core `createStore` method is deprecated!** + +`createStore` will continue to work indefinitely, but we discourage direct use of `createStore` or the original `redux` package. + +Instead, you should use [the `configureStore` method](https://redux-toolkit.js.org/api/configureStore) from our official [Redux Toolkit](https://redux-toolkit.js.org) package, which wraps `createStore` to provide a better default setup and configuration approach. You should also use Redux Toolkit's [`createSlice` method](https://redux-toolkit.js.org/api/createSlice) for writing reducer logic. + +Redux Toolkit also re-exports all of the other APIs included in the `redux` package as well. + +See the [**Migrating to Modern Redux** page](../usage/migrating-to-modern-redux.mdx) for details on how to update your existing legacy Redux codebase to use Redux Toolkit. + +::: + +## Top-Level Exports + +- [createStore(reducer, preloadedState?, enhancer?)](createStore.md) - [combineReducers(reducers)](combineReducers.md) - [applyMiddleware(...middlewares)](applyMiddleware.md) - [bindActionCreators(actionCreators, dispatch)](bindActionCreators.md) - [compose(...functions)](compose.md) -### Store API +## Store API - [Store](Store.md) - [getState()](Store.md#getState) - [dispatch(action)](Store.md#dispatchaction) - [subscribe(listener)](Store.md#subscribelistener) - [replaceReducer(nextReducer)](Store.md#replacereducernextreducer) - -### Importing - -Every function described above is a top-level export. You can import any of them like this: - -#### ES6 - -```js -import { createStore } from 'redux' -``` - -#### ES5 (CommonJS) - -```js -var createStore = require('redux').createStore -``` - -#### ES5 (UMD build) - -```js -var createStore = Redux.createStore -``` diff --git a/docs/api/applyMiddleware.md b/docs/api/applyMiddleware.md index 777d31bf30..fa69236709 100644 --- a/docs/api/applyMiddleware.md +++ b/docs/api/applyMiddleware.md @@ -9,22 +9,32 @@ description: 'API > applyMiddleware: extending the Redux store' # `applyMiddleware(...middleware)` +## Overview + Middleware is the suggested way to extend Redux with custom functionality. Middleware lets you wrap the store's [`dispatch`](Store.md#dispatchaction) method for fun and profit. The key feature of middleware is that it is composable. Multiple middleware can be combined together, where each middleware requires no knowledge of what comes before or after it in the chain. +:::warning Warning + +You shouldn't have to call `applyMiddleware` directly. Redux Toolkit's [`configureStore` method](https://redux-toolkit.js.org/api/configureStore) automatically adds a default set of middleware to the store, or can accept a list of middleware to add. + +::: + The most common use case for middleware is to support asynchronous actions without much boilerplate code or a dependency on a library like [Rx](https://github.com/Reactive-Extensions/RxJS). It does so by letting you dispatch [async actions](../understanding/thinking-in-redux/Glossary.md#async-action) in addition to normal actions. For example, [redux-thunk](https://github.com/reduxjs/redux-thunk) lets the action creators invert control by dispatching functions. They would receive [`dispatch`](Store.md#dispatchaction) as an argument and may call it asynchronously. Such functions are called _thunks_. Another example of middleware is [redux-promise](https://github.com/acdlite/redux-promise). It lets you dispatch a [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) async action, and dispatches a normal action when the Promise resolves. -Middleware is not baked into [`createStore`](createStore.md) and is not a fundamental part of the Redux architecture, but we consider it useful enough to be supported right in the core. This way, there is a single standard way to extend [`dispatch`](Store.md#dispatchaction) in the ecosystem, and different middleware may compete in expressiveness and utility. +The original Redux [`createStore`](createStore.md) method does not understand what middleware are out of the box - it has to be configured with `applyMiddleware` to add that behavior. However, Redux Toolkit's [`configureStore` method](https://redux-toolkit.js.org/api/configureStore) automatically adds middleware support by default. -#### Arguments +## Arguments - `...middleware` (_arguments_): Functions that conform to the Redux _middleware API_. Each middleware receives [`Store`](Store.md)'s [`dispatch`](Store.md#dispatchaction) and [`getState`](Store.md#getState) functions as named arguments, and returns a function. That function will be given the `next` middleware's dispatch method, and is expected to return a function of `action` calling `next(action)` with a potentially different argument, or at a different time, or maybe not calling it at all. The last middleware in the chain will receive the real store's [`dispatch`](Store.md#dispatchaction) method as the `next` parameter, thus ending the chain. So, the middleware signature is `({ getState, dispatch }) => next => action`. -#### Returns +### Returns (_Function_) A store enhancer that applies the given middleware. The store enhancer signature is `createStore => createStore` but the easiest way to apply it is to pass it to [`createStore()`](./createStore.md) as the last `enhancer` argument. +## Examples + #### Example: Custom Logger Middleware ```js @@ -191,7 +201,7 @@ export default connect(state => ({ }))(SandwichShop) ``` -#### Tips +## Tips - Middleware only wraps the store's [`dispatch`](Store.md#dispatchaction) function. Technically, anything a middleware can do, you can do manually by wrapping every `dispatch` call, but it's easier to manage this in a single place and define action transformations on the scale of the whole project. diff --git a/docs/api/bindActionCreators.md b/docs/api/bindActionCreators.md index e3e5496e82..7579323e64 100644 --- a/docs/api/bindActionCreators.md +++ b/docs/api/bindActionCreators.md @@ -9,6 +9,8 @@ description: 'API > bindActionCreators: wrapping action creators for dispatching # `bindActionCreators(actionCreators, dispatch)` +## Overview + Turns an object whose values are [action creators](../understanding/thinking-in-redux/Glossary.md#action-creator), into an object with the same keys, but with every action creator wrapped into a [`dispatch`](Store.md#dispatchaction) call so they may be invoked directly. Normally you should just call [`dispatch`](Store.md#dispatchaction) directly on your [`Store`](Store.md) instance. If you use Redux with React, [react-redux](https://github.com/gaearon/react-redux) will provide you with the [`dispatch`](Store.md#dispatchaction) function so you can call it directly, too. @@ -17,17 +19,23 @@ The only use case for `bindActionCreators` is when you want to pass some action For convenience, you can also pass an action creator as the first argument, and get a dispatch wrapped function in return. -#### Parameters +:::warning Warning + +This was originally intended for use with the legacy React-Redux `connect` method. It still works, but is rarely needed. + +::: + +## Parameters 1. `actionCreators` (_Function_ or _Object_): An [action creator](../understanding/thinking-in-redux/Glossary.md#action-creator), or an object whose values are action creators. 2. `dispatch` (_Function_): A [`dispatch`](Store.md#dispatchaction) function available on the [`Store`](Store.md) instance. -#### Returns +### Returns (_Function_ or _Object_): An object mimicking the original object, but with each function immediately dispatching the action returned by the corresponding action creator. If you passed a function as `actionCreators`, the return value will also be a single function. -#### Example +## Example #### `TodoActionCreators.js` @@ -103,9 +111,3 @@ function TodoListContainer(props) { export default connect(state => ({ todos: state.todos }))(TodoListContainer) ``` - -#### Tips - -- You might ask: why don't we bind the action creators to the store instance right away, like in classical Flux? The problem is that this won't work well with universal apps that need to render on the server. Most likely you want to have a separate store instance per request so you can prepare them with different data, but binding action creators during their definition means you're stuck with a single store instance for all requests. - -- If you use ES5, instead of `import * as` syntax you can just pass `require('./TodoActionCreators')` to `bindActionCreators` as the first argument. The only thing it cares about is that the values of the `actionCreators` properties are functions. The module system doesn't matter. diff --git a/docs/api/combineReducers.md b/docs/api/combineReducers.md index 424a53a71b..88130d34c0 100644 --- a/docs/api/combineReducers.md +++ b/docs/api/combineReducers.md @@ -9,11 +9,31 @@ description: 'API > combineReducers: merging slice reducers to create combined s # `combineReducers(reducers)` -As your app grows more complex, you'll want to split your [reducing function](../understanding/thinking-in-redux/Glossary.md#reducer) into separate functions, each managing independent parts of the [state](../understanding/thinking-in-redux/Glossary.md#state). +## Overview -The `combineReducers` helper function turns an object whose values are different reducing functions into a single reducing function you can pass to [`createStore`](createStore.md). +The `combineReducers` helper function turns an object whose values are different "slice reducer" functions into a single combined reducer function you can pass to Redux Toolkit's [`configureStore`](https://redux-toolkit.js.org/api/configureStore) (or the legacy [`createStore`](createStore.md) method) + +The resulting combined reducer calls every slice reducer any time an action is dispatched, and gathers their results into a single state object. This enables splitting up reducer logic into separate functions, each managing their own slice of the state independently. + +:::tip + +This should be rarely needed - Redux Toolkit's [`configureStore` method](https://redux-toolkit.js.org/api/configureStore) will automatically call `combineReducers` for you if you pass in an object of slice reducers: + +```ts +const store = configureStore({ + reducer: { + posts: postsReducer, + comments: commentsReducer + } +}) +``` + +You can still call `combineReducers()` yourself if you need to construct the root reducer manually first. + +::: + +### State Slices -The resulting reducer calls every child reducer, and gathers their results into a single state object. **The state produced by `combineReducers()` namespaces the states of each reducer under their keys as passed to `combineReducers()`** Example: @@ -33,23 +53,24 @@ rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) You can control state key names by using different keys for the reducers in the passed object. For example, you may call `combineReducers({ todos: myTodosReducer, counter: myCounterReducer })` for the state shape to be `{ todos, counter }`. -A popular convention is to name reducers after the state slices they manage, so you can use ES6 property shorthand notation: `combineReducers({ counter, todos })`. This is equivalent to writing `combineReducers({ counter: counter, todos: todos })`. +## Arguments -> ##### A Note for Flux Users -> -> This function helps you organize your reducers to manage their own slices of state, similar to how you would have different Flux Stores to manage different state. With Redux, there is just one store, but `combineReducers` helps you keep the same logical division between reducers. +1. `reducers` (_Object_): An object whose values correspond to different reducer functions that need to be combined into one. -#### Arguments - -1. `reducers` (_Object_): An object whose values correspond to different reducing functions that need to be combined into one. See the notes below for some rules every passed reducer must follow. +```ts +combineReducers({ + posts: postsReducer, + comments: commentsReducer +}) +``` -> Earlier documentation suggested the use of the ES6 `import * as reducers` syntax to obtain the reducers object. This was the source of a lot of confusion, which is why we now recommend exporting a single reducer obtained using `combineReducers()` from `reducers/index.js` instead. An example is included below. +See the notes below for some rules every passed reducer must follow. -#### Returns +### Returns (_Function_): A reducer that invokes every reducer inside the `reducers` object, and constructs a state object with the same shape. -#### Notes +## Notes This function is mildly opinionated and is skewed towards helping beginners avoid common pitfalls. This is why it attempts to enforce some rules that you don't have to follow if you write the root reducer manually. @@ -59,11 +80,11 @@ Any reducer passed to `combineReducers` must satisfy these rules: - It must never return `undefined`. It is too easy to do this by mistake via an early `return` statement, so `combineReducers` throws if you do that instead of letting the error manifest itself somewhere else. -- If the `state` given to it is `undefined`, it must return the initial state for this specific reducer. According to the previous rule, the initial state must not be `undefined` either. It is handy to specify it with ES6 optional arguments syntax, but you can also explicitly check the first argument for being `undefined`. +- If the `state` given to it is `undefined`, it must return the initial state for this specific reducer. According to the previous rule, the initial state must not be `undefined` either. It is handy to specify it with optional arguments syntax, but you can also explicitly check the first argument for being `undefined`. While `combineReducers` attempts to check that your reducers conform to some of these rules, you should remember them, and do your best to follow them. `combineReducers` will check your reducers by passing `undefined` to them; this is done even if you specify initial state to `Redux.createStore(combineReducers(...), initialState)`. Therefore, you **must** ensure your reducers work properly when receiving `undefined` as state, even if you never intend for them to actually receive `undefined` in your own code. -#### Example +## Example #### `reducers/todos.js` @@ -130,8 +151,8 @@ console.log(store.getState()) // } ``` -#### Tips +## Tips -- This helper is just a convenience! You can write your own `combineReducers` that [works differently](https://github.com/acdlite/reduce-reducers), or even assemble the state object from the child reducers manually and write a root reducing function explicitly, like you would write any other function. +- This helper is just a convenience! You can write your own `combineReducers` that [works differently](https://github.com/redux-utilities/reduce-reducers), or even assemble the state object from the child reducers manually and write a root reducer function explicitly, like you would write any other function. - You may call `combineReducers` at any level of the reducer hierarchy. It doesn't have to happen at the top. In fact you may use it again to split the child reducers that get too complicated into independent grandchildren, and so on. diff --git a/docs/api/compose.md b/docs/api/compose.md index fc390987c5..476b09fc88 100644 --- a/docs/api/compose.md +++ b/docs/api/compose.md @@ -9,20 +9,28 @@ description: 'API > compose: composing multiple functions together' # `compose(...functions)` +## Overview + Composes functions from right to left. This is a functional programming utility, and is included in Redux as a convenience. -You might want to use it to apply several [store enhancers](../understanding/thinking-in-redux/Glossary.md#store-enhancer) in a row. +You might want to use it to apply several [store enhancers](../understanding/thinking-in-redux/Glossary.md#store-enhancer) in a row. `compose` is also usable as a general-purpose standalone method. + +:::warning Warning + +You shouldn't have to call `compose` directly. Redux Toolkit's [`configureStore` method](https://redux-toolkit.js.org/api/configureStore) automatically configures a Redux store with the standard `applyMiddleware` and Redux DevTools store enhancers, and offers an `enhancers` argument to pass in additional enhancers. + +::: -#### Arguments +## Arguments 1. (_arguments_): The functions to compose. Each function is expected to accept a single parameter. Its return value will be provided as an argument to the function standing to the left, and so on. The exception is the right-most argument which can accept multiple parameters, as it will provide the signature for the resulting composed function. -#### Returns +### Returns (_Function_): The final function obtained by composing the given functions from right to left. -#### Example +## Example This example demonstrates how to use `compose` to enhance a [store](Store.md) with [`applyMiddleware`](applyMiddleware.md) and a few developer tools from the [redux-devtools](https://github.com/reduxjs/redux-devtools) package. @@ -38,6 +46,6 @@ const store = createStore( ) ``` -#### Tips +## Tips - All `compose` does is let you write deeply nested function transformations without the rightward drift of the code. Don't give it too much credit! diff --git a/docs/api/createStore.md b/docs/api/createStore.md index c663130228..24009cb651 100644 --- a/docs/api/createStore.md +++ b/docs/api/createStore.md @@ -7,14 +7,28 @@ description: 'API > createStore: creating a core Redux store'   -# `createStore(reducer, [preloadedState], [enhancer])` +# `createStore(reducer, preloadedState?, enhancer?)` Creates a Redux [store](Store.md) that holds the complete state tree of your app. There should only be a single store in your app. -#### Arguments +:::danger -1. `reducer` _(Function)_: A [reducing function](../understanding/thinking-in-redux/Glossary.md#reducer) that returns the next [state tree](../understanding/thinking-in-redux/Glossary.md#state), given the current state tree and an [action](../understanding/thinking-in-redux/Glossary.md#action) to handle. +**The original Redux core `createStore` method is deprecated!** + +`createStore` will continue to work indefinitely, but we discourage direct use of `createStore` or the original `redux` package. + +Instead, you should use [the `configureStore` method](https://redux-toolkit.js.org/api/configureStore) from our official [Redux Toolkit](https://redux-toolkit.js.org) package, which wraps `createStore` to provide a better default setup and configuration approach. You should also use Redux Toolkit's [`createSlice` method](https://redux-toolkit.js.org/api/createSlice) for writing reducer logic. + +Redux Toolkit also re-exports all of the other APIs included in the `redux` package as well. + +See the [**Migrating to Modern Redux** page](../usage/migrating-to-modern-redux.mdx) for details on how to update your existing legacy Redux codebase to use Redux Toolkit. + +::: + +## Arguments + +1. `reducer` _(Function)_: A root [reducer function](../understanding/thinking-in-redux/Glossary.md#reducer) that returns the next [state tree](../understanding/thinking-in-redux/Glossary.md#state), given the current state tree and an [action](../understanding/thinking-in-redux/Glossary.md#action) to handle. 2. [`preloadedState`] _(any)_: The initial state. You may optionally specify it to hydrate the state from the server in universal apps, or to restore a previously serialized user session. If you produced `reducer` with [`combineReducers`](combineReducers.md), this must be a plain object with the same shape as the keys passed to it. Otherwise, you are free to pass anything that your `reducer` can understand. @@ -49,7 +63,21 @@ console.log(store.getState()) // [ 'Use Redux', 'Read the docs' ] ``` -#### Tips +## Deprecation and Alternate `legacy_createStore` Export + +In [Redux 4.2.0, we marked the original `createStore` method as `@deprecated`](https://github.com/reduxjs/redux/releases/tag/v4.2.0). Strictly speaking, **this is _not_ a breaking change**, nor is it new in 5.0, but we're documenting it here for completeness. + +**This deprecation is solely a _visual_ indicator that is meant to encourage users to [migrate their apps from legacy Redux patterns to use the modern Redux Toolkit APIs](https://redux.js.org/usage/migrating-to-modern-redux)**. The deprecation results in a visual strikethrough when imported and used, like ~~`createStore`~~, but with _no_ runtime errors or warnings. + +**`createStore` will continue to work indefinitely, and will _not_ ever be removed**. But, today we want _all_ Redux users to be using Redux Toolkit for all of their Redux logic. + +To fix this, there are three options: + +- **[Follow our strong suggestion to switch over to Redux Toolkit and `configureStore`](../usage/migrating-to-modern-redux.mdx)** +- Do nothing. It's just a visual strikethrough, and it doesn't affect how your code behaves. Ignore it. +- Switch to using the `legacy_createStore` API that is now exported, which is the exact same function but with no `@deprecated` tag. The simplest option is to do an aliased import rename, like `import { legacy_createStore as createStore } from 'redux'` + +## Tips - Don't create more than one store in an application! Instead, use [`combineReducers`](combineReducers.md) to create a single root reducer out of many. diff --git a/docs/api/utils.md b/docs/api/utils.md new file mode 100644 index 0000000000..c85a345fe3 --- /dev/null +++ b/docs/api/utils.md @@ -0,0 +1,22 @@ +--- +id: utils +title: Additional Utilities +hide_title: true +description: 'API > utils: Additional utility functions' +--- + +  + +# Utility Functions + +The Redux core exports additional utility functions for reuse. + +## `isAction` + +Returns true if the parameter is a valid Redux action object (a plain object with a string `type` field). + +This also serves as a TypeScript type predicate, which will narrow the TS type to `Action`. + +## `isPlainObject` + +Returns true if the value appears to be a plain JS object. diff --git a/docs/faq/Actions.md b/docs/faq/Actions.md index 8ce950fb8a..b47691c628 100644 --- a/docs/faq/Actions.md +++ b/docs/faq/Actions.md @@ -114,7 +114,7 @@ There are [many async/side effect middlewares available](https://github.com/mark As a general rule of thumb: - Thunks are best for complex synchronous logic (especially code that needs access to the entire Redux store state), and simple async logic (like basic AJAX calls). With the use of `async/await`, it can be reasonable to use thunks for some more complex promise-based logic as well. -- Sagas are best for complex async logic and decoupled "background thread"-type behavior, especially if you need to listen to dispatched actions (which is something that can't be done with thunks). They require familiarity with ES6 generator functions and `redux-saga`'s "effects" operators. +- Sagas are best for complex async logic and decoupled "background thread"-type behavior, especially if you need to listen to dispatched actions (which is something that can't be done with thunks). They require familiarity with generator functions and `redux-saga`'s "effects" operators. - Observables solve the same problems as sagas, but rely on RxJS to implement async behavior. They require familiarity with the RxJS API. We recommend that most Redux users should start with thunks, and then add an additional side effect library like sagas or observables later if their app really requires handling for more complex async logic. @@ -131,7 +131,7 @@ Since sagas and observables have the same use case, an application would normall **Discussions** - [Reddit: discussion of using thunks and sagas together, and pros and cons of sagas](https://www.reddit.com/r/reactjs/comments/8vglo0/react_developer_map_by_adamgolab/e1nr597/) -- [Stack Overflow: Pros/cons of using redux-saga with ES6 generators vs redux-thunk with ES2017 async/await](https://stackoverflow.com/questions/34930735/pros-cons-of-using-redux-saga-with-es6-generators-vs-redux-thunk-with-es2017-asy) +- [Stack Overflow: Pros/cons of using redux-saga with ES2015 generators vs redux-thunk with ES2017 async/await](https://stackoverflow.com/questions/34930735/pros-cons-of-using-redux-saga-with-es6-generators-vs-redux-thunk-with-es2017-asy) - [Stack Overflow: Why use Redux-Observable over Redux-Saga?](https://stackoverflow.com/questions/40021344/why-use-redux-observable-over-redux-saga/40027778#40027778) ### Should I dispatch multiple actions in a row from one action creator? diff --git a/docs/faq/General.md b/docs/faq/General.md index 48c8d3761e..5109f42ebf 100644 --- a/docs/faq/General.md +++ b/docs/faq/General.md @@ -104,7 +104,11 @@ Redux can be used as a data store for any UI layer. The most common usage is wit ## Do I need to have a particular build tool to use Redux? -Redux is originally written in ES6 and transpiled for production into ES5 with Webpack and Babel. You should be able to use it regardless of your JavaScript build process. Redux also offers a UMD build that can be used directly without any build process at all. The [counter-vanilla](https://github.com/reduxjs/redux/tree/master/examples/counter-vanilla) example demonstrates basic ES5 usage with Redux included as a `