'Second unwanted call made to browser history in JS

In my application, I need to redirect using browserHistory created from this package. In order to pass some context, I need to add a query string to the URL. The format in which I receive these params is in JSON. So before being able to call history.push, I first need to stringify and encode the given JSON.

Here's an example:

import { createBrowserHistory } from 'history';

const browserHistory = createBrowserHistory({ window });

const given = { x: 1 };

browserHistory.push(`/foo?test-param=${encodeURIComponent(JSON.stringify(given))}`);

This redirection works...BUT it breaks the native browser back button (sort of). You see what happens is that encodeURIComponent(JSON.stringify(given)) outputs %7B%22x%22%3A1%7D. As soon as the browser sees the %3A part, it automatically changes it into a : in the URL bar. This causes an additional change in the browserHistory, meaning that two URL changes happened:

  1. /foo?test-param=%7B%22x%22%3A1%7D which is the wanted outcome generated by the above code
  2. /foo?test-param=%7B%22x%22:1%7D which is automatically done by the browser (decoding %3A)

The issue now is that when I press the browser's back button, it tries to go to the second last URL which is the one given in step 1 above and not the original referrer URL. This then re-triggers a conversion to step 2. So the only way to get back to the referral URL is to hit the back button 2 times in quick succession.

Has anybody encountered this before?

PS: If I opt for encodeURI instead of encodeURIComponent above, it works. The reason for this is that encodeURI does not encode : to begin with.



Solution 1:[1]

If this is going in Emails, and the URL want to have JSON embeded somehow, I would use Hex. This should avoid any conflicts with special chars..

eg..

const given = { x: 1 };

const toHex = s => [...new TextEncoder().encode(s)].map(m => m.toString(16)).join('');

const fromHex = h => 
  (new TextDecoder()).decode(
  new Uint8Array(
  h.match(/.{1,2}/g).map(v => parseInt(v, 16)))); 

const url = `/foo?testparam=${toHex(JSON.stringify(given))}`;

console.log(url);
console.log(JSON.parse(fromHex('7b2278223a317d')));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Keith