Releases: reduxjs/redux
v3.0.2
One of the problems v3.0.1 was intended to solve was not completely fixed in 3.0.1.
This release amends that.
- An error thrown inside reducer during the
combineReducer()
sanity check will be delayed until the first dispatch. This lets Redux DevTools correctly handle it and recover from the error. (#717, #761, #807, reduxjs/redux-devtools#106, reduxjs/redux-devtools#120)
To enjoy this, you’ll need at least redux-devtools@2.1.4
.
v3.0.1
This release brings a few changes that, while aimed at making the developer experience better, should have no noticeable impact on your apps.
- In development,
combineReducers()
now checks the state shape on every invocation instead of just once. This makes it easy to discover issues like #715 where you post-process the state fromcombineReducers()
and then wonder why something you added to it has disappeared after the next action. This change has no effect in production, and you’re unlikely to hit it in development either, as post-processing state fromcombineReducers()
isn't a very common pattern. (#715, #720) - The reducer probing performed by
combineReducers()
as a sanity check has been moved from thecombineReducers()
call to the first reducer invocation. Technically the check is still performed during thecombineReducers()
call, but the error is only thrown after during the initial dispatch. (Usually that would correspond to thecreateStore()
call.) This reason for this is that otherwise an error thrown inside a reducer used to blow upcombineReducers()
and thus the whole module definition, and hot reloading used to fail in this case instead of showing the error in Redux DevTools. With this change and a corresponding fix in Redux DevTools, an error thrown inside a reducer that was later wrapped incombineReducers()
will be displayed correctly, and once you fix the error, it will hot reload. (#717, #761, reduxjs/redux-devtools#106, reduxjs/redux-devtools#120) - The reducer probing performed by
combineReducers()
as a sanity check now uses@@redux/PROBE_UNKNOWN_ACTION_
prefix in the action type. Previously, action type was a random string. The reason for this change is to make it easier for type-checking libraries like redux-tcomb ignore such actions. This change does not affect application developers: just like before, you should never attempt to handle@@redux/*
actions directly in your code. (#792, #796)
Happy reducing!
v3.0.0
v2.0.0
Breaking Changes
getReducer()
is removed from Store public API (#668)
It was only needed because React Redux used it to magically allow reducer hot reloading. This magic used to cause problems (#301, #340), so we made the usage of replaceReducer()
for hot reloading explicit (#667).
It is likely that you never used getReducer()
. However, if you used hot reloading of reducers, you'll need to add module.hot
API calls to the place where you create the store. (Of course, assuming that you use Webpack.)
Before
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
export default function configureStore(initialState) {
return createStore(rootReducer, initialState);
}
After
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers/index');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
This is more code, but what's happening is now explicit, and this also lets you do this right in index.js
instead of creating a separate <Root>
component. See #667 for a migration example.
compose()
now acts like a proper compose()
(#669)
This fixes the embarrassing mishap that compose()
API was.
As suggested by @jlongster, now it is a proper compose()
you'd find in Lodash or Underscore:
it('composes from right to left', () => {
const double = x => x * 2;
const square = x => x * x;
expect(compose(square)(5)).toBe(25);
expect(compose(square, double)(5)).toBe(100);
expect(compose(double, square, double)(5)).toBe(200);
});
In terms of usage, you just need to remove createStore
from the chain, and add it as a function application instead.
Before
const finalCreateStore = compose(
applyMiddleware(stuff),
devTools(),
somethingElse(),
createStore
);
After
const finalCreateStore = compose(
applyMiddleware(stuff),
devTools(),
somethingElse()
)(createStore);
process.env.NODE_ENV
is required for CommonJS build (#671)
In 1.0.1, we temporarily removed the dependency on it to support React Native, but now that RN 0.10 is out with process.env.NODE_ENV
polyfill, we again demand it to be defined. If you're not ready to use RN 0.10, or are in a different environment, either use a browser build, or shim it yourself.
v1.0.1
v1.0.0
Breaking Changes
If you're migrating from 0.12, apply 0.12 -> 1.0.0-alpha migration first, and then 1.0.0-alpha -> 1.0.0-rc. 1.0 only has a single breaking change since 1.0.0-rc:
- If
dispatch
is attempted while reducer is executing, an error is thrown. Note that you can dispatch from lifecycle hooks just fine. It's only reducers that are not allowed to dispatch. (#368)
New Home
We moved under rackt Github org. This won't affect you, but the new official URL is https://github.com/rackt/redux. We did this because we share values, and we want to collaborate on creating better tools, libraries, documentation, and examples. Redux stays independent of React, but we will work closely with React Router community to figure out a better integration.
Docs!
We have real docs now. There are a few missing pieces, but it's a terrific effort, so thanks to everybody who contributed in the past month to get this shipped. Thanks to Gitbook for providing great tooling, too.
Examples!
There's been no shortage of great examples in Awesome Redux, but we're shipping two new built-in examples in 1.0. One of them is a very simple async application. Creating it is covered in async tutorial. Another example we ship is a “real-world” example. It's a port of somewhat well-known flux-react-router-example to Redux, and shows advanced techniques such as caching, data normalization, custom API middleware, and pagination. Hopefully this example will help answer some commonly asked questions.
Other Improvements
- Unsubscribing during a dispatch is now fixed: #462
bindActionCreators
now can also accept a function as the first argument: #352- Dispatching from iframe now works: #304
- Symbols can be used as action types: #295 (Note: we don't recommend you to do this, because they're not serializable, so you can't record/replay user sessions.)
Patrons
The work on Redux was funded by the community.
Meet some of the outstanding companies and individuals that made it possible:
v1.0.0-rc
NOTE: THIS IS A PRE-RELEASE WITH SOME BREAKING CHANGES.
THE README IS NOT UPDATED TO ITS API, SEE EXAMPLES AND TESTS INSTEAD.
Compatible example code: https://github.com/gaearon/redux/tree/v1.0.0-rc/examples
Compatible test code: https://github.com/gaearon/redux/tree/v1.0.0-rc/test
Changes in these release build on top of changes in 1.0 alpha release, so check them out first.
Big Changes
React-specific code has been moved to react-redux and will be versioned separately
This means that <Provider>
, @provide
, <Connector>
, @connect
are all there. This means you need to replace 'redux/react'
with react-redux
and redux/react-native
with react-redux/native
in your dependency tree, as well as add react-redux
as an explicit dependency if you use React. For global builds, you'll get Redux
from this library and ReactRedux
from the other library. They should work together well.
Link: #230
1.0 alpha
import { createStore } from 'redux';
import { Provider } from 'redux/react'; // React
import { Provider } from 'redux/react-native'; // React Native
1.0 RC
import { createStore } from 'redux';
import { Provider } from 'react-redux'; // React
import { Provider } from 'react-redux/native'; // React Native
createStore
no longer implicitly combines reducers
Now you have to use combineReducers
explicitly to combine several reducer functions into a single reducer.
Link: #257
1.0 alpha
import { createStore } from 'redux';
import * as reducers from '../reducers';
const store = createStore(reducers);
1.0 RC
import { createStore, combineReducers } from 'redux';
import * as reducers from '../reducers';
const reducer = combineReducers(reducers)
const store = createStore(reducer);
All middleware is now “smart” middleware
All middleware now accepts { getState, dispatch }
as the first parameter. This means that, if your middleware already accepted ({ dispatch, getState })
, you don't need to change anything, but otherwise you need to wrap your middleware into one more function.
Link: #213
1.0 alpha
// “Dumb” middleware
export function log(next) {
return (action) => {
console.log(action);
return next(action);
};
}
// “Smart” middleware
export function thunk({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ?
action(dispatch, getState) :
next(action);
}
1.0 RC
// “Dumb” middleware is wrapped in one more function
export function log(/* { dispatch, getState } */) {
return next => action => {
console.log(action);
return next(action);
};
}
// “Smart” middleware stays the same
export function thunk({ dispatch, getState }) {
return (next) => (action) =>
typeof action === 'function' ?
action(dispatch, getState) :
next(action);
}
createStore
no longer accepts middleware
You need to use a dedicated applyMiddleware(...middlewares)
function that turns a vanilla createStore
into a middleware-capable createStore
.
Link: #213
1.0 alpha
import { createStore } from 'redux';
import * as reducers from '../reducers';
const store = createStore(
reducers,
initialState,
({ getState, dispatch }) => [thunk({ getState, dispatch }), logger]
);
1.0 RC
import { createStore, combineReducers, applyMiddleware } from 'redux';
import * as reducers from '../reducers';
const reducer = combineReducers(reducers);
const finalCreateStore = applyMiddleware(thunk, logger)(createStore);
const store = finalCreateStore(reducer, initialState);
The thunk middleware is no longer included by default
If you use “async action creator” form where an action creator returns a function with dispatch, getState => ...
signature, now you need to add redux-thunk
as a dependency and explicitly pass it to applyMiddleware
.
Link: #256
1.0 alpha
import { createStore } from 'redux';
import * as reducers from '../reducers';
const store = createStore(reducers, initialState);
store.dispatch(incrementAsync());
1.0 RC
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import * as reducers from '../reducers';
const reducer = combineReducers(reducers);
const finalCreateStore = applyMiddleware(thunk)(createStore);
const store = finalCreateStore(reducer, initialState);
store.dispatch(incrementAsync());
Correctness Changes
combineReducers
now throws if you return undefined
state
Previously, reducers could return undefined
as a valid value. Unfortunately it's too easy to do this by mistake by putting an early return
into a reducer, or by forgetting to specify a default
case inside the switch
statement. The new behavior is to throw in combineReducers
if you return undefined
while handling an action or initializing. If undefined
is a valid state for your reducer, consider using null
instead.
1.0 alpha
function toggle(state, action) {
switch (action.type) {
case SET_ON:
return true;
case SET_OFF:
return undefined;
}
}
1.0 RC
function toggle(state = false, action) {
switch (action.type) {
case SET_ON:
return true;
case SET_OFF:
return false;
default:
return state;
}
}
combineReducers
throws if you have no default
case
Handling @@INIT
action is an anti-pattern. It's internal to Redux, and you should never handle it directly. It is renamed to @@redux/INIT
in Redux 1.0 RC. In addition, Redux now throws if your reducer does not return an initial state in response to a randomized action type.
If you used @@INIT
action to return the initial state, you should instead return it when the state
passed as the first argument is undefined
, regardless of the action type. You should remove any reference to @@INIT
action type from your code.
If you used @@INIT
action to transform rehydrated state from server (for example, to turn plain objects into immutable maps), you need to do this by inspecting state
instead. For example, see how redux-example
fixed this problem.
1.0 alpha
function counter(state, action) {
switch (action.type) {
case '@@INIT': // You could get away with this in alpha
return 0;
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
}
}
function immutable(state = Immutable.fromJS({}), action) {
switch (action.type) {
case '@@INIT': // You could get away with this in alpha
return Immutable.fromJS(state);
case DO_SOMETHING:
return state.merge(something);
}
}
1.0 RC
function counter(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state; // Will be probed by a random action
}
}
function immutable(state = {}, action) {
if (!Map.isMap(state) && !List.isList(state)) {
state = Immutable.fromJS(state);
}
switch (action.type) {
case DO_SOMETHING:
return state.merge(something);
default:
return state; // Will be probed by a random action
}
}
(React) Components now update correctly in response to the actions fired in componentDidMount
Link: #208
Dispatch from the middleware sends the dispatch through the whole middleware chain
Link: #250
v1.0.0-alpha
NOTE: THIS IS A PRE-RELEASE WITH SOME BREAKING CHANGES.
THE README IS NOT UPDATED TO ITS API, SEE EXAMPLES AND TESTS INSTEAD.
Compatible example code: https://github.com/gaearon/redux/tree/v1.0.0-alpha/examples
Compatible test code: https://github.com/gaearon/redux/tree/v1.0.0-alpha/test
Full diff (includes changes to the examples, tests and implementation):
https://github.com/rackt/redux/compare/v0.12.0...v1.0.0-alpha?diff=unified
Naming
- “Stateless Stores” are now called reducers. (#137 (comment))
- The “Redux instance” is now called “The Store”. (#137 (comment))
- The dispatcher is removed completely. (#166 (comment))
API changes
is nowcomposeStores
composeReducers
.is gone.createDispatcher
is nowcreateRedux
createStore
.<Provider>
now acceptsstore
prop instead of.redux
- The new
createStore
signature iscreateStore(reducer: Function | Object, initialState: any, middlewares: Array | ({ getState, dispatch }) => Array)
. - If the first argument to
createStore
is an object,composeReducers
is automatically applied to it. - The “smart” middleware signature changed. It now accepts an object instead of a single
getState
function. Thedispatch
function lets you “recurse” the middleware chain and is useful for async: #113 (comment).
Correctness changes
- The
dispatch
provided by the default thunk middleware now walks the whole middleware chain. - It is enforced now that raw Actions at the end of the middleware chain have to be plain objects.
- Nested dispatches are now handled gracefully. (#110, #119)
Internal changes
- The object in React context is renamed from
toredux
store
. - Some tests are rewritten for clarity, focus and edge cases.
- Redux in examples is now aliased to the source code for easier work on master.
v0.12.0
No breaking changes this time.
- Classes returned by decorators now expose a static
DecoratedComponent
property for easier testing - Dependencies on
lodash
andbabel-runtime
are dropped - Now compatible with Babel loose mode
composeStore
now ignores non-function values (useful in Babel loose mode)- A UMD build is added
- The initial action dispatched to the stores now has a built-in
@@INIT
type (might be useful to devtools)