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

Tracking progress of the promise in reducers #10

Open
anatoliyarkhipov opened this issue Jul 30, 2015 · 3 comments
Open

Tracking progress of the promise in reducers #10

anatoliyarkhipov opened this issue Jul 30, 2015 · 3 comments

Comments

@anatoliyarkhipov
Copy link

Hi. The Promise middleware is very cool, but what if we add the progress tracking too? It may looks like this:

import { isFSA } from 'flux-standard-action';

function isPromise(val) {
  return val && typeof val.then === 'function';
}

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }

    function progress(percent = 0) {
        dispatch({ ...action, payload: { percent }, progress: true });
    }

    if (isPromise(action.payload)) {

        progress();

        return action.payload.then(
          result => dispatch({ ...action, payload: result }),
          error => dispatch({ ...action, payload: error, error: true }),
          percent => progress(percent)
        );
    }

    return next(action);
  };
}

Even if the promise do not supports the progress callback, the first call of it can be useful to determine that we must display some kind of gif-spinner.

Usage example:

// Post Reducers

const displayPostReducer = handleAction('DISPLAY_POST', (state, action) => {
    // do something for display post
})

const updatePostReducer = handleAction('UPDATE_POST', (state, action) => {
    // do something for update post
})

const deletePostReducer = handleAction('DELETE_POST', (state, action) => {
    // do something for delete post
})

const progressTypes = ['DISPLAY_POST', 'UPDATE_POST', 'DELETE_POST']

const handleProgress = handleProgress(progressTypes , (state, { type, meta }) => {

    // This callback will be runned only if (action.progress == true)

    return (type == 'UPDATE_POST' || action.type == 'DELETE_POST')
        // If it's an update or delete action, then we must block form
        // only if the current post is the same with the deleted/updated post
        ? { ...state, progress: (state.post.id == meta.id) }
        : { ...state, progress: true }
})

const postReducer = (state = { post: null, progress: false }, action) => {

    state = handleProgress(state, action)

    if (state.progress) {

        return state
    }

    state = displayPostReducer(state, action)
    state = updatePostReducer(state, action)
    state = deletePostReducer(state, action)

    return state
}

// Post Actions

const buildMeta = (id) => { id }
const displayPost = createAction('DISPLAY_POST', (id) => api.getById(id), buildMeta)
const updatePost  = createAction('UPDATE_POST',  (id, data) => api.update(id, data), buildMeta)
const deletePost  = createAction('DELETE_POST',  (id) => api.delete(id), buildMeta)

// Post Components

class PostGrid extends Component {

    // ...

    render() {

        const { posts, displayPost, deletePost } = this.props

        return (
            <table>
                {_.map(posts, (post) => 

                    <tr onClick={() => displayPost(post.id)}>
                        <td>{post.title}</td>
                        <td onClick={() => deletePost(post.id)}>Delete</td>
                    </tr>
                )}
            </table>
        )
    }
}

@connect( state => ({ post: state.post, progress: state.progress }) )
class PostForm extends Component {

    // ...

    render() {

        const { progress, post } = this.props

        if (progress) {
            // display the gif-spinner 
        }

        // ...
    }
}
@anatoliyarkhipov
Copy link
Author

We can also add the progress key to the reducerMap instead of using some special handleProgress function:

handleAction('FETCH_DATA', {
  next(state, action) {...}
  throw(state, action) {...}
  progress(state, action) {...}
});

@rstacruz
Copy link

Keep in mind though that progress is deprecated and not part of the Promises/A+ standard.

@hafeez1042
Copy link

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants