From fffbe312526c5742f704c77ddfc614b070150665 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 22 Jul 2015 21:20:24 +0300 Subject: [PATCH] Add JSDoc annotations --- .eslintrc | 2 + src/createStore.js | 80 +++++++++++++++++++++++++++++++-- src/index.js | 6 +-- src/utils/applyMiddleware.js | 12 +++-- src/utils/bindActionCreators.js | 15 +++++++ src/utils/combineReducers.js | 18 +++++++- src/utils/compose.js | 9 ++-- src/utils/isPlainObject.js | 4 ++ src/utils/mapValues.js | 7 +++ src/utils/pick.js | 7 +++ 10 files changed, 147 insertions(+), 13 deletions(-) diff --git a/.eslintrc b/.eslintrc index 90e39033120..c501c8bf4cf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,6 +6,8 @@ "node": true }, "rules": { + "valid-jsdoc": 2, + "react/jsx-uses-react": 2, "react/jsx-uses-vars": 2, "react/react-in-jsx-scope": 2, diff --git a/src/createStore.js b/src/createStore.js index f7b78ca9fe4..2cbfd1b18b8 100644 --- a/src/createStore.js +++ b/src/createStore.js @@ -1,13 +1,34 @@ import invariant from 'invariant'; import isPlainObject from './utils/isPlainObject'; -// Don't ever try to handle these action types in your code. They are private. -// For any unknown actions, you must return the current state. -// If the current state is undefined, you must return the initial state. +/** + * These are private action types reserved by Redux. + * For any unknown actions, you must return the current state. + * If the current state is undefined, you must return the initial state. + * Do not reference these action types directly in your code. + */ export var ActionTypes = { INIT: '@@redux/INIT' }; +/** + * Creates a Redux store that holds the state tree. + * The only way to change the data in the store is to call `dispatch()` on it. + * + * There should only be a single store in your app. To specify how different + * parts of the state tree respond to actions, you may combine several reducers + * into a single reducer function by using `combineReducers`. + * + * @param {Function} reducer A function that returns the next state tree, given + * the current state tree and the action to handle. + * + * @param {any} initialState 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. + * + * @returns {Store} A Redux store that lets you read the state, dispatch actions + * and subscribe to changes. + */ export default function createStore(reducer, initialState) { invariant( typeof reducer === 'function', @@ -18,10 +39,23 @@ export default function createStore(reducer, initialState) { var currentState = initialState; var listeners = []; + /** + * Reads the state tree managed by the store. + * + * @returns {any} The current state tree of your application. + */ function getState() { return currentState; } + /** + * Adds a change listener. It will be called any time an action is dispatched, + * and some part of the state tree may potentially have changed. You may then + * call `getState()` to read the current state tree inside the callback. + * + * @param {Function} listener A callback to be invoked on every dispatch. + * @returns {Function} A function to remove this change listener. + */ function subscribe(listener) { listeners.push(listener); @@ -31,6 +65,28 @@ export default function createStore(reducer, initialState) { }; } + /** + * Dispatches an action. It is the only way to trigger a state change. + * + * The `reducer` function the store was created with will be called with the + * current state tree and the and the given `action`. Its return value will + * be considered the next state of the tree, and the change listeners will be + * notified. + * + * The base implementation only supports plain object actions. If you want to + * dispatch a promise, an observable, a thunk, or something else, you need to + * wrap your store creating function into the corresponding middleware. For + * example, see the documentation for the `redux-thunk` package. Even the + * middleware will eventually dispatch plain object actions using this method. + * + * @param {Object} action A plain object representing “what changed”. It is + * a good idea to keep actions serializable so you can record and replay user + * sessions, or use the time travelling Redux developer tools. + * + * @returns {Object} For convenience, the same action object you dispatched. + * Note that, if you use a custom middleware, it may wrap `dispatch()` to + * return something else (for example, a Promise you can await). + */ function dispatch(action) { invariant( isPlainObject(action), @@ -42,10 +98,28 @@ export default function createStore(reducer, initialState) { return action; } + /** + * Returns the reducer currently used by the store to calculate the state. + * + * It is likely that you will only need this function if you implement a hot + * reloading mechanism for Redux. + * + * @returns {Function} The reducer used by the current store. + */ function getReducer() { return currentReducer; } + /** + * Replaces the reducer currently used by the store to calculate the state. + * + * You might need this if your app implements code splitting and you want to + * load some of the reducers dynamically. You might also need this if you + * implement a hot reloading mechanism for Redux. + * + * @param {Function} nextReducer The reducer for the store to use instead. + * @returns {void} + */ function replaceReducer(nextReducer) { currentReducer = nextReducer; dispatch({ type: ActionTypes.INIT }); diff --git a/src/index.js b/src/index.js index 0679d67aabe..6bfc4d80f9b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,13 @@ import createStore from './createStore'; -import compose from './utils/compose'; import combineReducers from './utils/combineReducers'; import bindActionCreators from './utils/bindActionCreators'; import applyMiddleware from './utils/applyMiddleware'; +import compose from './utils/compose'; export { createStore, - compose, combineReducers, bindActionCreators, - applyMiddleware + applyMiddleware, + compose }; diff --git a/src/utils/applyMiddleware.js b/src/utils/applyMiddleware.js index 0f320f293e3..729330263cd 100644 --- a/src/utils/applyMiddleware.js +++ b/src/utils/applyMiddleware.js @@ -1,11 +1,17 @@ import compose from './compose'; /** - * Creates a higher-order store that applies middleware to a store's dispatch. + * Creates a higher-order store that applies middleware to the dispatch method + * of the Redux store. This is handy for a variety of tasks, such as expressing + * asynchronous actions in a concise manner, or logging every action payload. + * + * See `redux-thunk` package as an example of the Redux middleware. + * * Because middleware is potentially asynchronous, this should be the first * higher-order store in the composition chain. - * @param {...Function} ...middlewares - * @return {Function} A higher-order store + * + * @param {...Function} middlewares The middleware chain to be applied. + * @return {Function} A higher-order store. */ export default function applyMiddleware(...middlewares) { return (next) => (reducer, initialState) => { diff --git a/src/utils/bindActionCreators.js b/src/utils/bindActionCreators.js index f9a81a5a800..a895a7ebb1c 100644 --- a/src/utils/bindActionCreators.js +++ b/src/utils/bindActionCreators.js @@ -1,5 +1,20 @@ import mapValues from '../utils/mapValues'; +/** + * Turns an object whose values are action creators, into an object with the + * same keys, but with every function wrapped into a `dispatch` call so they + * may be invoked directly. This is just a convenience method, as you can call + * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. + * + * @param {Object} actionCreators An object whose values are action creator + * functions. One handy way to obtain it is to use ES6 `import * as` syntax. + * + * @param {Function} dispatch The `dispatch` function available on your Redux + * store. + * + * @returns {Object} The object mimicking the original object, but with every + * action creator wrapped into the `dispatch` call. + */ export default function bindActionCreators(actionCreators, dispatch) { return mapValues(actionCreators, actionCreator => (...args) => dispatch(actionCreator(...args)) diff --git a/src/utils/combineReducers.js b/src/utils/combineReducers.js index 84904e7c954..e59e4d0976b 100644 --- a/src/utils/combineReducers.js +++ b/src/utils/combineReducers.js @@ -13,6 +13,22 @@ function getErrorMessage(key, action) { ); } +/** + * Turns an object whose values are different reducer functions, into a single + * reducer function. It will call every child reducer, and gather their results + * into a single state object, whose keys correspond to the keys of the passed + * reducer functions. + * + * @param {Object} reducers An object whose values correspond to different + * reducer functions that need to be combined into one. One handy way to obtain + * it is to use ES6 `import * as reducers` syntax. The reducers may never return + * undefined for any action. Instead, they should return their initial state + * if the state passed to them was undefined, and the current state for any + * unrecognized action. + * + * @returns {Function} A reducer function that invokes every reducer inside the + * passed object, and builds a state object with the same shape. + */ export default function combineReducers(reducers) { var finalReducers = pick(reducers, (val) => typeof val === 'function'); @@ -38,7 +54,7 @@ export default function combineReducers(reducers) { ); }); - return function composition(state = {}, action) { + return function combination(state = {}, action) { return mapValues(finalReducers, (reducer, key) => { var newState = reducer(state[key], action); invariant( diff --git a/src/utils/compose.js b/src/utils/compose.js index 4db0884d3b3..a3fd8be5186 100644 --- a/src/utils/compose.js +++ b/src/utils/compose.js @@ -1,7 +1,10 @@ /** - * Composes functions from left to right - * @param {...Function} funcs - Functions to compose - * @return {Function} + * Composes functions from left to right. + * + * @param {...Function} funcs - The functions to compose. + * @return {Function} A function that passes its only argument to the first of + * the `funcs`, then pipes its return value to the second one, and so on, until + * the last of the `funcs` is called, and its result is returned. */ export default function compose(...funcs) { return funcs.reduceRight((composed, f) => f(composed)); diff --git a/src/utils/isPlainObject.js b/src/utils/isPlainObject.js index e42b4a6e728..a5896655e5e 100644 --- a/src/utils/isPlainObject.js +++ b/src/utils/isPlainObject.js @@ -1,3 +1,7 @@ +/** + * @param {any} obj The object to inspect. + * @returns {boolean} True if the argument appears to be a plain object. + */ export default function isPlainObject(obj) { if (!obj) { return false; diff --git a/src/utils/mapValues.js b/src/utils/mapValues.js index 29d203cf616..bb87ac551d3 100644 --- a/src/utils/mapValues.js +++ b/src/utils/mapValues.js @@ -1,3 +1,10 @@ +/** + * Applies a function to every key-value pair inside an object. + * + * @param {Object} obj The source object. + * @param {Function} fn The mapper function taht receives the value and the key. + * @returns {Object} A new object that contains the mapped values for the keys. + */ export default function mapValues(obj, fn) { return Object.keys(obj).reduce((result, key) => { result[key] = fn(obj[key], key); diff --git a/src/utils/pick.js b/src/utils/pick.js index 2c9719c1c01..518c055e616 100644 --- a/src/utils/pick.js +++ b/src/utils/pick.js @@ -1,3 +1,10 @@ +/** + * Picks key-value pairs from an object where values satisfy a predicate. + * + * @param {Object} obj The object to pick from. + * @param {Function} fn The predicate the values must satisfy to be copied. + * @returns {Object} The object with the values that satisfied the predicate. + */ export default function pick(obj, fn) { return Object.keys(obj).reduce((result, key) => { if (fn(obj[key])) {