Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSDoc annotations to the public API #300

Merged
merged 1 commit into from
Jul 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
80 changes: 77 additions & 3 deletions src/createStore.js
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good way to document callbacks is to use the @callback tag. This way you can document the @params of the callback function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I didn't know this! Want to send a PR?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I don't really have time tonight though. Guess I can do it after the merge?

Edit: Oh, its merged already ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah feel free to make a PR against breaking-changes-1.0 branch.

* @returns {Function} A function to remove this change listener.
*/
function subscribe(listener) {
listeners.push(listener);

Expand All @@ -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),
Expand All @@ -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 });
Expand Down
6 changes: 3 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -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
};
12 changes: 9 additions & 3 deletions src/utils/applyMiddleware.js
Original file line number Diff line number Diff line change
@@ -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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. For now I'd avoid hardcoding URL because we might move that package to a separate Github organization soon.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably wise

*
* 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.
* @returns {Function} A higher-order store.
*/
export default function applyMiddleware(...middlewares) {
return (next) => (reducer, initialState) => {
Expand Down
15 changes: 15 additions & 0 deletions src/utils/bindActionCreators.js
Original file line number Diff line number Diff line change
@@ -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))
Expand Down
18 changes: 17 additions & 1 deletion src/utils/combineReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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(
Expand Down
9 changes: 6 additions & 3 deletions src/utils/compose.js
Original file line number Diff line number Diff line change
@@ -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.
* @returns {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));
Expand Down
4 changes: 4 additions & 0 deletions src/utils/isPlainObject.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
7 changes: 7 additions & 0 deletions src/utils/mapValues.js
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
7 changes: 7 additions & 0 deletions src/utils/pick.js
Original file line number Diff line number Diff line change
@@ -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])) {
Expand Down