React - Re-direct to a new domain - react-router

I have an app built in react and I am trying to implement re-direct where I need to redirect the user from my app base URL to a completely new domain.
Example:
React app base URL - "localhost:3001"
Redirect to - "www.google.com"
I am using react routes in my app.
ReactDOM.render(
(
If I use Redirect as above, it redirects to "http://localhost:3001/#/http:/www.google.com"
How to get rid of base URL while redirecting to a new domain?

React-route only allows redirect to other routes, you can confirm this by reading Github issue here
So the answer is NO, YOU CAN'T.
If I'm not misunderstanding your intention here, you want to redirect whatever which is not being handled by the router.
One aproach that comes to my mind as a solution:
A component-dedicated: Create a component dedicated for this route with a componentDidMount similar to this:
componentDidMount() {
window.location.assign('http://github.com');
}
As an alternative, you can catch it directly in the server and redirect there.
But definitively you need to look for an alternative solutoin due react-route does not allow redirecting to externals url

Expanding on an excellent answer by Facino La Rocca
import React, {Component} from 'react';
class DomainUrlRedirect extends Component{
componentDidMount(){
const{searchParams, pathname, url} = this.props
let uri = new URL(url);
uri.pathname = pathname
for(const paramName in searchParams){
uri.searchParams.append(paramName, searchParams[paramName])
}
window.location.assign(uri.href)
}
render(){
return(<span data-test-component="DomainUrlRedirect"></span>)
}
}
export default DomainUrlRedirect
You can leverage URL utility supported by modern browsers to construct your URL properly.
Also, don't forget that the component will still try to render. Render it as part of some other component on condition so that object that uses it does not disappear.
render(){
const{ redirectNow } = this.props;
return(<div>
<p>Important content</p>
{redirectNow &&
<DomainUrlRedirect {...url_params}/>
}
</div>)
}

Related

Redirect to external or internal link

I have a page in my application where backend decides where user should be redirected next. And it can be external link like 'google.com' or internal '/'. I use useNavigate hook from react-router-dom.
import { useNavigate } from "react-router-dom";
import axios from "axios";
useEffect(async () => {
await axios.get().then((response) => {
navigate(axios.redirectLink);
});
});
It perfectly works for internal links, but doesn't work for navigate('https://google.com'). Then it tries to open https://my-website/https://google.com. What might be a solution here?
There is no solution in navigating if you want a use Link tag do that else you can check this link as well
React-Router open Link in new tab
<Link to="your url" target="_blank">Test</Link>

Can a React app return an image string that can be read into the SRC attribute of a <img> tag?

Is it possible to reference a React App that is running on another server using
<img src="https://www.react_app.com">
The idea is that the React App returns an image string (or similar) like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA ...
So that it can be read in a <img src=""> tag?
The main question is what React code simply sends back a request with the string so that it can be read in src=""?
Also is there a timeout for how long an <img src=""> attempts to fetch an image?
React component imports
import React, { useCallback, useEffect, useState, useRef } from 'react'
import classNames from 'classnames'
import { fabric } from 'fabric'
import fabricConfig from './fabricConfig'
import FileUploader from './components/FileUploader'
import ColorPicker from './components/ColorPicker'
import Checkbox from './components/Checkbox'
import Button from './components/Button'
import getRatio from './utils/getRatio'
import getInitialCanvasSize from './utils/getInitialCanvasSize'
import getImageFromURL from './utils/getImageFromURL'
import resizeCanvas from './utils/resizeCanvas'
import removeSelectedElements from './utils/removeSelectedElements'
import getCanvasObjectFilterRGB from './utils/getCanvasObjectFilterRGB'
import setAttributes from './utils/setAttributes'
import { Z, Y, DELETE } from './utils/constants'
Fetch image from URL and automatically make changes to it on load
const imageUrl = "www.something.com/image"
if (imageUrl) {
new Promise(resolve => fabric.loadSVGFromURL(imageUrl, (objects, options) => {
const group = new fabric.Group(objects)
resolve(getRatio(group, canvas))
}))
.then(({ ratio, width, height }) => {
fabric.loadSVGFromURL(imageUrl, (objects, options) => {
try {
objects.forEach(obj => {
setAttributes(obj, {
left: (obj.left * ratio) + ((canvas.width / 2) - ((width * ratio) / 2)),
top: (obj.top * ratio) + ((canvas.height / 2) - ((height * ratio) / 2)),
})
obj.scale(ratio)
// MAKE EDITS TO THE SVG OBJECT HERE
canvas.add(obj)
})
canvas.renderAll()
// HERE I AM TRYING TO SAVE THE CANVAS STATE AND SEND IT BACK TO THE THIRD PARTY WEBSITE USING GET PARAMETERS
var canvasImg = ''
if(urlParams.get("export") === "png"){
canvasImg = canvas.toDataURL("image/png")
} else if (urlParams.get("export") === "pdf") {
canvasImg = canvas.toDataURL("image/pdf")
} else {
onCanvasModified(canvas)
}
} catch(err) {
console.log('Could not retrieve that image')
}
})
})
What you want is a CDN, which serves image assets via a GET request (the img src accepts a string which it uses to fetch (GET) content). In short, a CDN serves the application with assets -- be it images, javascript, CSS or HTML. A React application is designed to update content in place via manipulating a virtual DOM; therefore, expecting it to serve assets across domains is anti-pattern. Instead, you would use a custom server (like express) or a web server (like nginx) to serve static assets.
As a simple example, imgur.com would the React application, while i.imgur.com would be their CDN to serve images like this and s.imgur.com would be their CDN to serve CSS/JS assets like this.
This answer goes into more detail how to do it; HOWEVER, this is only one of many, many ways on how accomplish the above, but the concept is still the same: Making a request to retrieve an image via an img src string.
I hesitate to provide full example code since I have no idea what stack you're working with and what your knowledge/comfort-level is regarding backend services. As such, if you want practice consuming a request that serves images on the frontend, then I'd recommend you start with this API service.
Example
Here's one of many ways to do it: Example Repo
To run the example...
1.) Clone the repo: git clone git#github.com:mattcarlotta/save-canvas-example.git
2.) Install dependencies: yarn or npm i
3.) Run yarn dev or npm dev
4.) Click one of the buttons to either save an image as PNG or as a PDF
The example includes quite a bit of notes, but to provide a brief:
User clicks button. File Ref
Depending on the clicked button, the canvas is converted to a Blob, which is then converted to a File. File Ref
This file is then sent (via POST) to an image microservice running at http://localhost:4000 listening for requests to /upload-file. File Ref
The microservice sees the request and directs to our middleware functions. File Ref
Then it directs it to the /upload-file controller. File Ref
The controller determines if the file upload was valid (in the middleware), if not it throws an error. File Ref
When valid, the file details are generated from req.file (this comes from our multer middleware function), a directory is created and a file is saved to that directory. File Ref
A filepath is then sent back to the client. File Ref
Client receives filepath from image microservice and sets it to state. File Ref
Client then renders a shareable link, a link to view the file, and a preview. File Ref
Results
Save PNG:
Save PDF:
Flow Diagram
I've tried to reproduce the project with minimal features. user can add and interact with rectangle and save the image. upon saving it would go to the server and the image data will be stored in a JSON file.
Here's the link to frontend: https://codesandbox.io/s/so-fabric-client-5bjsf
As you have mentioned, there are two different react apps; I've created two routes, /draw where the user can draw the image and /images where I fetch the images. you can consider these two routes as different react projects since the logic remains the same regardless of their origin.
On the backend side, for demonstration purposes and simplicity, I've used a JSON file and sending all the file content in response when the application wants to display the images. It could become problematic once there are hundreds of images or when you want to search them by the user. so consider using a database or any other method.
here's the backend code:
const express = require("express");
const path = require("path");
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname + "/build")));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.post("/save-image", (req, res) => {
const image = req.body.image
fs.readFile('images.json', function (err, data) {
if(err) {
console.log(err)
res.status(500)
}
var json = JSON.parse(data)
json.push({id: json.length, image})
fs.writeFile("images.json", JSON.stringify(json), (err, result) => {
if(err) console.log(err);
})
})
res.status(200);
})
app.get("/get-images", (req, res) => {
res.json(JSON.parse(fs.readFileSync("./images.json")))
})
app.listen(PORT, () => {
console.log(`up and running on port ${PORT}`);
})
images.json is just a file with [] as its content. the code is pretty much self-explainatory, I've uploaded all the code on GitHub as well -- so-fabric-demo and you can check the demo on Heroku

Prefetch resources for async routes

Is there a way to prefetch or preload async routes? I'm exploring how to do this right now with RR2/3. The basic idea is I code split on every route but I'd like to be able to cache bundles for connected pages in a service worker before visiting that page. So what I'd like to do is have a custom <Link> that every time it's rendered, it tries to cache the resources of the page it's linked to. This would make page transitions considerably faster. What I don't know is if there's a way to emulate navigating to a route so that the resources will be fetched. Is there an API for this or some sort of tricky way to do this someone can think of?
This is what I came up. It's a component that wraps the React Router Link component and in componentDidMount (so only runs on the client not the server) check if in production (no need to run this during development) and if this is a browser that doesn't support Service Workers (this check is specific to my use case). Then manually match against the location and call any async getComponent functions.
import React from 'react'
import Link from 'react-router/lib/Link'
class GatsbyLink extends React.Component {
componentDidMount () {
// Only enable prefetching of Link resources in production and for browsers that
// don't support service workers *cough* Safari/IE *cough*.
if (process.env.NODE_ENV === 'production' && !('serviceWorker' in navigator)) {
const routes = require('my-routes')
const { createMemoryHistory } = require('history')
const matchRoutes = require('react-router/lib/matchRoutes')
const getComponents = require('react-router/lib/getComponents')
const createLocation = createMemoryHistory().createLocation
if (typeof routes !== 'undefined') {
matchRoutes([routes], createLocation(this.props.to), (error, nextState) => {
getComponents(nextState, () => console.log('loaded bundle(s) for ' + this.props.to))
})
}
}
}
render () {
return <Link {...this.props} />
}
}
module.exports = GatsbyLink
You could just do a require.ensure... section in a timeout when the Link is mounted. That should require the code split and load it up async. The timeout will ensure it get's loaded in a separate file.
I would recommend using RR4 for code splitting as I found in RR3 the async required routes get re-included and re-rendered if a child route changes. In my case, I had the componentWillMount of my routes being fired for any child route changes. e.g. Navigating from /agent/step-1 to /agent/step-2 will cause the Component for /agent to be unmounted and re-mounted.

Aurelia hook into router before app.js

So the aurelia app gets bootstrapped from the main.js
bootstrap(function(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
aurelia.start().then(() => aurelia.setRoot('app', document.body));
});
The source code tells me there is a router() method you can call on the FrameworkConfiguration which is what aurelia.use gives you.
But the implementation gives you no hooks, it just includes the framework-templating-router.
The reason I want a hook in, is because I want to do an api call in the activate() of the app.js however, I wish to slide in a Navigation Pipeline middleware before the api call is made.
configureRouter is called after activate() which is obvious. This means I can dynamically inject menu items (api behind authorised walls) to build up my initial screen. If I get a 401 it rejects the activate() promise - which I want - however what would be better is if the pipeline step could Redirect('login').
If I were to put custom login the activate() then I would have two places of redirection, but also it just doesn't fit into the aurelia design very well.
There is an alternative solution which is to defer configuring the router till a child view of the app.js but it doesn't seem as nice.
In your app.js, you can do this:
import {Router} from 'aurelia-router';
#inject(Router)
export class App {
constructor (router) {
this.router = router;
this.router.configure(config => ...);
}
activate () {
...
}
}
Essentially....you don't have to use the configureRouter method. It's just there as a convenience. Using the constructor allows more flexibility in this case.

Why my page will not change when use history.push using react-router

I have a single page application webpage...
Below is some of my code...
var createBrowserHistory = require('history/lib/createBrowserHistory');
var history = createBrowserHistory();
history.push('account/person');
When I click a button, I can see the browser's URL change to http://IP/account/person, but nothing happened, still on same page.
But if I replace url directly to http://IP/account/person by typing, the page will map to correct page.
Below is my react router config
<Router history={new createBrowserHistory()}>
<Route path='/account' component={Gift}></Route>
<Route path='/account/person' component={Person}></Route>
</Router>
Why this happened?
I think you need to import a different object to handle the programmatic routing:
https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md
import { browserHistory } from 'react-router'
browserHistory.push('/account/person')