@gaearon gaearon released this Jul 13, 2015

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.

Links: #173, #191, #193, #197, #259

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.

Links: #186, #259

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

Assets 2