diff --git a/redux-thunk/redux-thunk-tests.ts b/redux-thunk/redux-thunk-tests.ts new file mode 100644 index 0000000000..56cf81f4fc --- /dev/null +++ b/redux-thunk/redux-thunk-tests.ts @@ -0,0 +1,127 @@ +/// +/// +/// +/// + +import { createStore, applyMiddleware, Store, Dispatch } from 'redux'; +import thunk from 'redux-thunk'; +import { ThunkInterface } from 'redux-thunk'; +import { Promise } from 'es6-promise'; + +declare var rootReducer: Function; +declare var fetch: any; + +// create a store that has redux-thunk middleware enabled +const createStoreWithMiddleware = applyMiddleware( + thunk +)(createStore); + +const store: Store = createStoreWithMiddleware(rootReducer); + +function fetchSecretSauce() { + return fetch('https://www.google.com/search?q=secret+sauce'); +} + +// These are the normal action creators you have seen so far. +// The actions they return can be dispatched without any middleware. +// However, they only express “facts” and not the “async flow”. + +function makeASandwich(forPerson: any, secretSauce?: any): any { + return { + type: 'MAKE_SANDWICH', + forPerson, + secretSauce + }; +} + +function apologize(fromPerson: any, toPerson?: any, error?: any): any { + return { + type: 'APOLOGIZE', + fromPerson, + toPerson, + error + }; +} + +function withdrawMoney(amount: number): any { + return { + type: 'WITHDRAW', + amount + }; +} + +// Even without middleware, you can dispatch an action: +store.dispatch(withdrawMoney(100)); + +// But what do you do when you need to start an asynchronous action, +// such as an API call, or a router transition? + +// Meet thunks. +// A thunk is a function that returns a function. +// This is a thunk. + +function makeASandwichWithSecretSauce(forPerson: any): ThunkInterface { + + // Invert control! + // Return a function that accepts `dispatch` so we can dispatch later. + // Thunk middleware knows how to turn thunk async actions into actions. + + return function (dispatch: Dispatch): any { + return fetchSecretSauce().then( + (sauce: any) => dispatch(makeASandwich(forPerson, sauce)), + (error: any) => dispatch(apologize('The Sandwich Shop', forPerson, error)) + ); + }; +} + +// Thunk middleware lets me dispatch thunk async actions +// as if they were actions! + +store.dispatch( + makeASandwichWithSecretSauce('Me') +); + +// It even takes care to return the thunk’s return value +// from the dispatch, so I can chain Promises as long as I return them. + +store.dispatch( + makeASandwichWithSecretSauce('My wife') +).then(() => { + console.log('Done!'); +}); + +// In fact I can write action creators that dispatch +// actions and async actions from other action creators, +// and I can build my control flow with Promises. + +function makeSandwichesForEverybody(): ThunkInterface { + return function (dispatch: Dispatch, getState: () => any): any { + if (!getState().sandwiches.isShopOpen) { + + // You don’t have to return Promises, but it’s a handy convention + // so the caller can always call .then() on async dispatch result. + + return Promise.resolve(); + } + + // We can dispatch both plain object actions and other thunks, + // which lets us compose the asynchronous actions in a single flow. + + return dispatch( + makeASandwichWithSecretSauce('My Grandma') + ).then(() => + Promise.all([ + dispatch(makeASandwichWithSecretSauce('Me')), + dispatch(makeASandwichWithSecretSauce('My wife')) + ]) + ).then(() => + dispatch(makeASandwichWithSecretSauce('Our kids')) + ).then(() => + dispatch(getState().myMoney > 42 ? + withdrawMoney(42) : + apologize('Me', 'The Sandwich Shop') + ) + ); + }; +} + diff --git a/redux-thunk/redux-thunk.d.ts b/redux-thunk/redux-thunk.d.ts new file mode 100644 index 0000000000..5e125e3bc9 --- /dev/null +++ b/redux-thunk/redux-thunk.d.ts @@ -0,0 +1,21 @@ +// Type definitions for redux-thunk +// Project: https://github.com/gaearon/redux-thunk +// Definitions by: Qubo +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + +declare module "redux-thunk" { + import { Middleware, Dispatch } from 'redux'; + + export interface Thunk extends Middleware { } + + export interface ThunkInterface { + (dispatch: Dispatch, getState?: () => T): any; + } + + var thunk: Thunk; + + export default thunk; +} +