reactjsredux-sagatus

How to yield inside an external callback function in react redux saga?


I'm using the tus Protocol to upload files and this protocol has some callback functions. What I'm trying to do is to yield an action and do something else, but I see that I can't use yield inside the callback.

Bellow is a piece of code from the saga function that contains the callback.

const upload = new tus.Upload(payload,
{
            endpoint: `${baseURL}files/`,
            chunkSize: fileChunkSize, 
            headers: { 'Authorization': `Bearer ${jwtToken}`, 'SelectedDatabase': selectedDatabase }, 
            onSuccess: () => {                        
                console.log("Upload Finished");
                toast.done(toastId);
                toastSuccess(`File (${payload.name}) was successfully uploaded.`);
                //yield put(sendFilesSuccess()); TODO: how to call this?
            }
}); 

upload.start();

I read something related to channels, but I couldn't understand it very well, so any help would be appreciated.


Solution

  • This is a great use case for Channels because they make handling event callbacks as if it were just like handling Redux actions. To wait and read from a channel, it's as simple as doing a take.

    function uploadChannelCreator(payload, /* ... */) {
      return eventChannel(emitter => {
          const upload = new tus.Upload(payload,
          {
                endpoint: `${baseURL}files/`,
                chunkSize: fileChunkSize, 
                headers: { 'Authorization': `Bearer ${jwtToken}`, 'SelectedDatabase': selectedDatabase }, 
                onSuccess: () => {
                    emitter('SUCCESS');
                }
                // ... Emit other messages based on different events (i.e. onFailure)
          }); 
    
          upload.start();
    
          return () => {}
        }
      )
    }
    
    function* uploadSaga(payload, /* ... */) {
        const chan = yield call(uploadChannelCreator, payload, /* ... */); // 1. Call Channel
        const msg = yield take(chan); // 2. Wait for channel to emit a message
    
        // Rest of code... (Add error handling if necessary).
        console.log("Upload Finished");
        toast.done(toastId);
        toastSuccess(`File (${payload.name}) was successfully uploaded.`);
    }