5

I want to open a url after receiving a success response from the api with the generated url. I'm trying to open that url inside the reducer like this:

window.open(url, '_blank');

this seems to be working fine for most of the browsers, but is not compatible in safari.

Any idea how can this be implemented to be cross browser compatible? Is there a way to do that with react-router-redux?

Thanks!

Update: I need to open an external url, not a route in the project so probably react-router is not the solution

parecementeria
  • 105
  • 1
  • 7
  • Do you get any errors in Safari? – U r s u s Jul 01 '16 at 11:08
  • I can not see any error but apparently window.open is not allowed in safari. There is another discussion http://stackoverflow.com/questions/20696041/window-openurl-blank-not-working-on-imac-safari explaining that. And I was wondering if maybe it will be possible to do that with react-router-redux – parecementeria Jul 01 '16 at 11:36

3 Answers3

6

According to official redux docs, in reducers you shouldn't:

  • Mutate its arguments;
  • Perform side effects like API calls and routing transitions;
  • Call non-pure functions, e.g. Date.now() or Math.random().

If you are using redux-thunk middleware to make api requests, it would be better to perform redirects there.

For example:

// actions.js

function asyncApiCall() {
  return function (dispatch) {
    return fetch('/api')
      .then(response => response.json().then(body => ({ response, body })))
      .then(({ response, body }) => {
        if (response.ok) {
          window.open(url, '_blank');
        }
      });
  };
}
1ven
  • 6,776
  • 1
  • 25
  • 39
3

The only javascript that is allowed to open a new window in Safari/Chrome is javascript directly attached to click handlers (and other direct user input handlers). In past versions people figured out some ways to cheat (like generating some other element -- a form or div -- and simulating user input with javascript), but newer versions are smarter about detecting this.

and you shouldnt contain this logic in reducers.

Maybe way is popup some modal and offer to user click button. you can do it in actions after data received, through dispatch action kind of {type:"OPEN_MODAL"}

Kokovin Vladislav
  • 10,241
  • 5
  • 38
  • 36
3

Reducers should be pure, therefore you should not try to open a new window there, instead you should create a middleware for all your fetch requests, then in a successful request you can redirect, open modals and whatever you need, for example:

import { push } from 'react-router-redux';

export default fetchMiddleware() {
 return ({ dispatch, getState }) => next => action => {
   if (!action.fetch) {
     return next(action);
   }

   const promise = fetch(action.fetch, { someOptionsHere })
     .then(response => response.json())
     .then(data => {
       if (whateverLogicYouNeedToRedirect) {
          return next(push('/go/somewhere/else'));
       }
       next({type: action.type, response: data});
     })
     .catch( error => {
        // check for errors in the response and
        // maybe redirect to login here
        next({type: 'FETCH_FAIL', error });
     });

   return promise;
 };
}

That's a simple middleware that fetch data from your API, then parse the JSON response, then based on your logic it can redirect to a different URL or just return an action with the fetch response, you would use this middleware from your action creators like this:

 export function loadData() {
   return {
     type: 'LOAD_SOMETHING',
     fetch: '/api/something/here',
   };
 }

you should improve the middleware to accept params, a way to define post, put, delete requests, and anything else you need, but I hope this give you an idea.

jaybee
  • 2,240
  • 14
  • 20
Crysfel
  • 7,926
  • 3
  • 33
  • 41
  • Thanks for your response, I'm using a middleaware so I will move the logic there, I also updated my question, it was not very clear. The problem is that window.open in not compatible with safari and apparently I can not use react-router for external urls. – parecementeria Jul 04 '16 at 10:29