Generating multiple links (URL) to display different states of my react App - reactjs

I have created a Single Page App with react and hosted it on netlify . I am making use of react-router to handle my routing. Is it possible to generate random routes (url) to send as a link for someone to view the current state of my app with a specific data ?
For Example:
wwww.forexample.com/myappshowing5dogs
--shows my app showing 5 dogs
wwww.forexample.com/myappshowing12catssandabook
--shows my app showing a different state of my app with 12 cats and a book
How can I generate links (url) to show all the countless possible states of my app?

You can use the useParams functionality of React-Router to do exactly that!
import {BrowserRouter, Route, useParams} from ReactRouterDOM
const Info = () => {
let {dogs, cats} = useParams();
return <div>There are {dogs || 0} dogs and {cats || 0} cats</div>;
}
const App = () => (
<BrowserRouter>
<Route path="/:dogs?/:cats?/" component={Info}/>
</BrowserRouter>)
ReactDOM.render(<App/>, document.getElementById('root'));
And to get Netlify to work nicely with these you need to add /* /index.html 200 into your _redirects file.
See my demo here:
Sourcecode shows how to do the redirects file and Routing

Related

Let only one page be server-side rendered with Meteor

My Meteor/React application should render one static page besides the reactive one pagers with reactive UIs. The static package does not even need to be "hydrated" with the React magic after displayed in the browser. Though the server-side rendering on the server will be dynamic with React components.
I got it working, but I'm not sure if it is the intended official way to do it.
File import/client/routes.js
...
<Route path="/reactive/pages/:id" component={ReactiveComponent} />
<Route path="/static_url" />
...
File server/main.jsx
...
onPageLoad((sink) => {
if (sink.request.path === '/static_url) {
sink.renderIntoElementById('app', renderToString(
<StaticPage />,
));
}
});
...
File client/main.js
...
import { Routes } from '../imports/client/routes';
Meteor.startup(() => {
...
if (window.location.pathname !== '/offer_pdf') {
render(Routes, document.getElementById('app'));
}
});
...
Especially when rendering dependent on the URI, it seems a little bit hacky to me. Does a more elegant solution exist?
I don't think there is anything official, but in general, of course, it's a good idea to use a router for rendering different pages, so I thought it worth pointing out that you can use react-router on the server as well:
import React from "react";
import { renderToString } from "react-dom/server";
import { onPageLoad } from "meteor/server-render";
import { StaticRouter } from 'react-router-dom';
import App from '/imports/ui/app.jsx';
onPageLoad(sink => {
const context = {};
sink.renderIntoElementById("app", renderToString(
<StaticRouter location={sink.request.url} context={context}>
<App />
</StaticRouter>
));
/* Context is written by the routes in the app. The NotFound route, used
when, uhm, no route is found, sets the status code. Here we set it on the
HTTP response to get a hard 404, not just a soft 404. Important for Google bot.
*/
context.statusCode && sink.setStatusCode(context.statusCode);
// add title to head of document if set by route
sink.appendToHead(`<title>${context.title || 'My page'}</title>`);
});
In App you can then use the usual Switch and Route tags for specifying different routes. In that you could, for instance, only specify routes that you want to be server-rendered.

React doesn't render on page refresh or manual URL entry

I've made two components. one is Articles.js and the another one is article.js. The router is in app.js. here is code from app.js:
<Router>
<Switch>
<Route exact path="/articles" component={Articles}>
</Route>
<Route path="/article/:id" component={Article}/>
</Switch>
</Router>
I've made link to "article/:id" in Articles component. If I click on the link in articles page, it works just fine, however, If I try to reload the page or manually enter id, for eg: "article/23", it will not render anything at all on the page.
I found some results on internet, but, most of them are either not relevant or are using hooks, which I cannot use with class components.
In componentDidMount function of Article compnent i am calling getData function which fetches data from the server and then after verfiying the response sends data here to this function :
initFunction = (ar)=>{
let data = ar.map(d=>{
return(
<tr><td>{d.id}</td><td>{d.title}</td><td>{moment(d.created_on).format('MMMM,Do YYYY')}</td><td>
<Link to={`article/${d.id}`}>Edit</Link> |
<Link to={`article/delete/${d.id}`}> Delete</Link>
</td></tr>
)
})
this.setState({
tableData:<>{data}</>
})
}
And this function just generates table rows and save them in state, which I use in render function to display the data. If I go to article page through this link, it will work fine but same link will not render anything if type it manually or reload it.
Here are the components I am importing in my component:
import ReactPaginate from 'react-paginate';
import { Link } from 'react-router-dom';
import { instanceOf } from 'prop-types';
import { withCookies, Cookies } from 'react-cookie';
import moment from 'moment'
The issue here is probably that your server is not set up to serve your app on the /article route.
When you click on the link in your app then react-router does not actually request make a request to your server for that route. Instead it updates the url and re-renders your app which picks up the new route.
When you load the route directly (such as on page reload or manually typing in the url bar) the react router has not been loaded yet. Instead the browser just blindly makes a request to that route. This is when your app is failing.
I had the same issue as you. I found the solution from this thread.
react-router dynamic segments crash when accessed
added into the of my index.html
what is initState of id, I think you need to store id to state and set initState is empty string, it work for me.
const [myId, setMyId] = useState("")

How to allow user to save inputs for further review

I have made a small calculating website and I would like the authenticated user to save his/her calculations.
Overview:
I have a main page with 8 input fields and several labels/graphs displaying results information. Whenever you change one of the inputs, the labels and graphs update thanks to hooks.
The goal: loading data
I made a "Save" button, which, when clicked :
saves all the inputs to firebase (this works fine already)
create a link in /myaccount/dashboard, which redirects you to the main page with your saved inputs (that I need help with)
I am having a hard time finding for resources online. However, while looking for responsive calculating websites, I came across this one : https://optionstrat.com
Even-thought I have no idea what they are calculating it does what I'm looking for, ie : you can "save a trade" and then go to you account where all the saved trades are displayed.
Does anyone know a good tutorial how to do so ?
Thanks you :)
Edit
This is my save function in my App.js:
function Savecalc(){
const calcRef = db.ref("User_"+auth.currentUser.uid);
const newCalc = calcRef.push();
newCalc.set({inputs:{a,b,c},outputs:{x,y}});
/* Then attribute an URL to a saved calculation*/
}
Then, in my Dashboard.js, I would have:
const db=app.database();
export default function Dashboard() {
/* getting the user calculations */
return (
<div>
<!-- Mapping of the user's calculations -->
</div>
)
I suspect what you are probably looking for is a little library called react-router-dom. This library essentially provides a collection of navigational components that you can employ to navigate around your app. Here is a basic example. Once you install it into your project, you should create a separate AppRouter.js file that might resemble something along the lines:
import React from 'react';
// Install the react-router-dom package
import { Router, Route, Switch } from 'react-router-dom';
// Further install the history package
import { createBrowserHistory } from 'history';
// Import your dashboard component and all other components you wish to create a route to
// This is just an example
import HomePage from '../components/Homepage';
import Dashboard from '../components/Dashboard';
// You will need to create a Page Not Found component that redirects when a wrong URL is inserted
import NotFoundPage from '../components/NotFoundPage';
export const history = createBrowserHistory();
const AppRouter = () => (
<Router history={history}>
<Switch>
<Route path='/' component={HomePage} exact={true} />
<Route path='/myaccount/dashboard' component={Dashboard} />
<Route component={NotFoundPage} />
</Switch>
</Router>
);
export default AppRouter;
Then in your main/app.js file you will need to add the AppRouter component. It should look something like this:
const Application = () => (
<Provider store={store}> // if using react-redux, otherwise ignore Provider
<AppRouter />
</Provider>
);
ReactDom.render(<Application />, document.getElementById('app'));
You can also create public and private routes (e.g. accessible only for signed in users). Examples of these can be found here.
If you are saving the user's calculations, you could then query them on whatever page you want to display them on e.g. /myaccount/dashboard.
You can then map over them and display them in the UI as links, where the link could be something like /myaccount/dashboard/YlxIJ2zOxI9KYJ5Dag6t where YlxIJ2zOxI9KYJ5Dag6t is the auto-generated document ID from Firestore.
Assuming you are using React Router, you can then have a route such as:
<Route exact path="/myaccount/dashboard/:id">
On this page, you can get the ID of the document from the parameters using React Router's useParams hook like so:
const { id } = useParams();
You can then query the specific calculation's info in useEffect, and display them however you want.
Let me know if this is what you need, or if you need more help with any step!

How to remove "/app" from route in gatsby

I have setup gatsby project and my routes only works when it contains /app because of this gatsby nodejs file.
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// page.matchPath is a special key that's used for matching pages
// only on the client.
if (page.path.match(/^\/app/)) {
page.matchPath = "/app/*"
// Update the page.
createPage(page)
}
}
I need to remove /app from my all the components. Is it possible to do so?
And also on production mode my dynamic routes does not work what is the issue?
const App = () => (
<Layout>
<Router>
<VerifyToken path="/app/:token"/>
<MagicLink path="/app/link/:magicLink"/>
</Router>
</Layout>
)
The code in your gatsby-node.js file is used to create what's called "client only routes". In the Gatsby docs they give an example that is used to create routes available only to logged-in users. These routes will exist on the client only and will not correspond to files in an app’s built assets on the server.
You could possibly eliminate the /app route if nothing is using it. But just based on the example component you posted, it does seem like your app is using it, and you did mention "dynamic routes". So you do need something to identify those routes and make them special to Gatsby.
In short, what this "client only route" is supposed to mean is that Gatsby will not create any server side pages for that route. But then you are supposed to create app components (mostly likely React components) that will respond to that route and build the content dynamically.
There is nothing special about the /app prefix. You can make it whatever you want as long as it is consistent between your Gatsby config and your client components and the router your client uses (hopefully Gastby's already built-in #reach/router?). But to create a hybrid app where some content is rendered server side by Gatsby and some content rendered dynamically client side, I'm pretty sure you need to have some kind of prefix to make that work so Gatsby can tell the difference between the two and how to render them. However, it's not clear from the Gatsby docs on page matchPath how that works. From the code example in the Gatsby docs, it does take a regular expression. It's possible that instead of /app you could try to match some other string somewhere else in the path instead of being at the beginning of the path. Whatever you do, the client side router will need to be set up to match that route too.
I don't think the Gatsby docs are that great so I can understand your struggle trying to get this to work. This Gatsby Mail App might help you to since it uses a different prefix (/threads) and has the complete source to an app that uses client side dynamic pages + static content.
And also on production mode my dynamic routes does not work what is
the issue?
Very hard to tell with the limited code you provided. That doesn't look like it's the full source to that component. Is your component named src/pages/app.js or src/pages/app/index.js? That's what it needs to be named to handle the routing for the /app prefix.
To enable client-only routes with no prefix (no /app), you setup Gatsby as below.
In gatsby-node.js:
exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions
if (page.path === `/`) {
page.matchPath = `/*`
createPage(page)
}
}
In index.js:
const IndexPage = () => {
return (
<Router>
<Home path="/" />
<OtherPage path="/other-page" />
</Router>
)
}
export default IndexPage
To remove /app from the route, you have to define your starting path named files in pages folder. For example, you want your home page to render with {your-domain}/home, just add a file named home.js in pages folder and import your component over there.
import React from "react";
import HomePage from "../features/home/Home.index";
const Home = () => (
<HomePage />
);
export default Home;

React router consume my url request to back-end API

I'm building an SPA react-redux app at client-side, asp.net core back-end API.
Everythings run perfectly with IISExpress when debugging.
But when I deploy to IIS as a web application nested default website with the alias "mysubdomain". Everything still runs ok except export function.
(1st case): Open browser, enter download API link: http://localhost/mysubdomain/api/v1/export?filterparam. A save dialogue open. That was my expectation.
(2nd case: normal case): open my site (homepage): http://localhost/mysubdomain then click export, a new window open by the link:
http://localhost/mysubdomain/api/v1/export?filterparam.
I was expecting that savefile popup opening similar to (1st case) but NO. Browse return my components/Layout rendered.
I don't know what happening with react router/route? Or I make something wrong? I guess that react-router just consume my URL-request then render my Component, instead of call to my back-end API.
My export function inside redux store:
export: (filterParams) => async (dispatch, getState) => {
const url = `api/v1/export?${filterParams}`;
window.open(url,'_blank');
}
Back-end API:
[HttpGet]
[Route("download")]
public async Task<IActionResult> Download(DailyDischargeUrlQuery urlQuery)
{
var stream = await _dailyDischargeRepository.ExportAsCsvStream(urlQuery.DischargeDate, urlQuery.MRN, urlQuery.GetCompanies(), urlQuery.GetOrders());
return File(stream, "text/csv", "dailydischarge.csv");
}
index.js
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const history = createBrowserHistory({ basename: baseUrl });
const initialState = window.initialReduxState;
const store = configureStore(history, initialState);
const rootElement = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
rootElement);
registerServiceWorker();
App.js
import 'core-js';
import React from 'react';
import {Route, Router} from 'react-router';
import Layout from './components/Layout';
import Home from './components/Home';
export default () => (
<Layout>
<Route exact path='/' component={Home}/>
</Layout>
);
The problem has been resolved!
That because of service worker built-in create-react-app.
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.

Resources