Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using put inside anonymous function callback

I am implementing Pusher into my React+Redux Saga application, but I am having a few problems with some callbacks where I can not hit the put(...) methods. Using console.log(...) etc. in the methods does show, but I am not able to put to the state of my application.

I could be wrong on some of the implementation of async/generator functions, but I am pretty much stuck right now.

My code to illustrate what will not fire:

import { takeLatest } from 'redux-saga'
import { call, put } from 'redux-saga/effects'

// Pusher actions
export const pusherConnecting = () => {
    return {
        type: ActionTypes.PUSHER_CONNECTING
    }
};

export const pusherConnectSucceeded = (client) => {
    return {
        type: ActionTypes.PUSHER_CONNECT_SUCCEEDED,
        client: client
    }
};

const pusherConnectFailed = (exception) => {
    return {
        type: ActionTypes.PUSHER_CONNECT_FAILED,
        message: exception
    }
};

// Pusher Saga
function * connectPusher(action) {
    try {
        const pusher = yield call(Api.connectPusher, action.directory, function(subscription) {
            subscription.bind(PUSHER_BIND_RELOAD, function() {
                location.reload(true);
            });

            subscription.bind(PUSHER_BIND_REQUEST_DATA, function(data) {
                if (data) {
                    put(updateDirectory(data));
                } else {
                    put(requestDirectory(action.directory.id));
                }
            });
        });

        pusher.connection.bind('connected', function() {
            put(pusherConnectSucceeded(pusher));
        });

        yield put(pusherConnecting());

    } catch (e) {
        yield put(pusherConnectFailed(e));
    }
}

export default function * pusherSaga() {
    yield * takeLatest(ActionTypes.DIRECTORY_FETCH_SUCCEEDED, connectPusher);
}



// My Api.ConnectPusher
export function * connectPusher(directory, subscription) {
    var pusher = new Pusher(PUSHER_KEY, {
        encrypted: true
    });

    var channels = ["test1", "test2"  ];

    for (var i = 0; i < channels.length; i++) {
        // Take each channel and callback with the subscription
        yield subscription(pusher.subscribe(channels[i]));
    }

    return pusher;
}

Solution based on @Sebastien

yield put(yield onConnect(pusher));

function onConnect(pusher) {
    return new Promise((resolve, reject) => {
        pusher.connection.bind('connected', function() {
            resolve(pusherConnectSucceeded(pusher));
        });
    });
}
like image 909
janhartmann Avatar asked Jun 09 '16 11:06

janhartmann


People also ask

Can you use an anonymous function in a callback?

In JavaScript, callbacks and anonymous functions can be used interchangeably.

How do you call a function inside a callback?

You need to use the . call() or . apply() methods on the callback to specify the context which the method is called upon. The callback method remote_submit does not know what this will be anymore and thus when it calls the callback methods they're executed like normal functions not on an object.

Can you pass a anonymous function as an argument to another function?

You can also pass an anonymous function as an argument into other function. You can also return an anonymous function from another function.

Can you assign an anonymous function to a variable and pass it as an argument to another function example?

As JavaScript supports Higher-Order Functions, we can also pass anonymous functions as parameters into another function. Example 3: In this example, we pass an anonymous function as a callback function to the setTimeout() method. This executes this anonymous function 2000ms later.


1 Answers

Redux-saga does not permit to put without using keyword yield. The put creates a simple json object/effect that must be interpreted/executed, and it won't if you don't yield.

Also, even with yield put(...), if this is done in a callback, it won't be interpreted, because Redux-saga does not have the ability to run callbacks in its interpreter. They'll simply be run as normal callbacks and nothing will happen.

If subscription.bind is supposed to return a single result, you can instead wrap that call into a function that returns a promise, and then yield that promise.

If subscription.bind is supposed to return a stream of results, you might need instead of create a channel. I guess in the future someone will ship something that can easily permits to transform Observables to Redux-saga streams

Note that if you don't need to unsubscribe/resubscribe multiple times, it may be simpler to you to put this code outside the saga, and just do

        subscription.bind(PUSHER_BIND_RELOAD, function() {
            location.reload(true);
        });

        subscription.bind(PUSHER_BIND_REQUEST_DATA, function(data) {
            if (data) {
                reduxStore.dispatch(updateDirectory(data));
            } else {
                reduxStore.dispatch((requestDirectory(action.directory.id));
            }
        });
like image 131
Sebastien Lorber Avatar answered Oct 18 '22 02:10

Sebastien Lorber