Meteor authentication and react-router - authentication

How do I get meteor to re-render my components when I sign in using the accounts-password package?
My react-router routes are:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Router, Route, IndexRoute, browserHistory } from 'react-router'
import App from './containers/App'
import Recordings from './containers/Recordings'
import LandingPage from './containers/LandingPage'
import { BloodPressure } from '../collections/BloodPressure'
const routes = (
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={LandingPage} />
<Route path="dashboard" component={Recordings} />
</Route>
</Router>
)
Meteor.startup( ()=> {
ReactDOM.render(routes, document.querySelector('.render-target'))
})
My App component is:
import React, { Component } from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { browserHistory } from 'react-router'
import Header from './Header'
class App extends Component {
constructor(props) {
super(props)
}
render() {
return(
<div>
<div className="ui four container">
<Header />
{this.props.children}
</div>
<div className="footer">
<p>Designed and Developed by Thomas Hoadley</p>
</div>
</div>
)
}
}
export default createContainer(() => {
return {
signedIn: Meteor.userId()
}
}, App)
When I sign in, I expect the page to automatically reload with the correct routes, however I am having to reload the page manually to redirect it.
Any help on this issue would be greatly appreciated!

Related

Why does react router work on localhost but will not work after Heroku deployment?

there are a few posts like this and I have tried other solutions such as adding a .htaccess file, and adding 'process.env.PUBLIC_URL' ahead of each route to get the relative route location, but nothing seems to be working with the new react-router-dom package which has been released...
The application will load the '/' login page no problem on startup in heroku, and in there a direct will move to another page, however after successful login I use 'window.location.assign("/Home")' to try to auto navigate back to the home page. This gets a 404 error.
Similarly, manually adding locations into the top bar also give 404 errors..
I have tried the fixes provided in other posts, but they are all for the old react-router-dom package (before they moved away from switch and started using BrowserRouter).
Can anyone help figure out why my router won't identify addresses added to the window location??
The application is currently deployed at https://dashboard.heroku.com/apps/octowatch-scratch
App.js
import React from "react";
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import AboutKPI from './pages/AboutKPI';
import Login from './pages/Login';
import BreachComposition from './pages/BreachComposition';
import BreachesPerWard from './pages/BreachesPerWard';
import Dashboard from './pages/Dashboard';
import AddAccount from './pages/AddAccount';
import ManageAccounts from './pages/ManageAccounts';
import OverallBreaches from './pages/OverallBreaches';
import AddData from './pages/AddData';
import Home from './pages/Home';
import { lightGreen, brown } from '#mui/material/colors';
import './App.css';
const theme = createTheme({
palette: {
primary: {main: lightGreen[900]},
secondary: {main: brown[500]},
},
});
function App() {
return (
<BrowserRouter>
<ThemeProvider theme={theme}>
<Routes>
<Route exact path="/" element = {<Login/>} />
<Route exact path="/AboutKPI" element = {<AboutKPI/>} />
<Route exact path="/BreachComposition" element = {<BreachComposition/>} />
<Route exact path="/BreachesPerWard" element = {<BreachesPerWard/>} />
<Route exact path="/Login" element = {<Login/>} />
<Route exact path="/Home" element = {<Home/>} />
<Route exact path="/Dashboard" element = {<Dashboard/>} />
<Route exact path="/AddAccount" element = {<AddAccount/>} />
<Route exact path="/ManageAccounts" element = {<ManageAccounts/>} />
<Route exact path="/OverallBreaches" element = {<OverallBreaches/>} />
<Route exact path="/AddData" element = {<AddData/>} />
</Routes>
</ThemeProvider>
</BrowserRouter>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
Login Page ()
import React, { useState } from "react";
import { Grid,Paper, Avatar, TextField, Button, Typography } from '#mui/material'
import FormControlLabel from '#mui/material/FormControlLabel';
import Checkbox from '#mui/material/Checkbox';
import Api from "../api.js"
import LockIcon from '#mui/icons-material/Lock';
import { Link } from 'react-router-dom';
export default function Login(breachesCallback) {
const paperStyle={padding :20,height:'70vh',width:280, margin:"20px auto"}
const avatarStyle={backgroundColor:'#1bbd7e'}
const btnstyle={margin:'8px 0'}
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
var now = new Date();
function validateForm() {
return email.length > 0 && password.length > 0;
}
function goHome () {
window.location.assign("/Home");
}
function goReset () {
window.location.assign("/UpdatePassword");
}
function handleSubmit(event) {
Api.noToken().post('/users/login', {
email: email,
password: password,
resetDate: Math.floor(((now / 8.64e7) - 150.604166666))
})
.then(function (response) {
if(response.status === 204){//this means a password reset still needs to occur here
Api.withToken().get('/breaches/')
.then(res => {
breachesCallback(res.data);
console.log(res.data);
}).then(goReset());
}
else if(response.status === 200){
window.sessionStorage.setItem("token", response.data.token);
Api.withToken().get('/breaches/')
.then(res => {
breachesCallback(res.data);
console.log(res.data);
}).then(goHome());
}
})
.catch(function (error) {
alert("Invalid username or password")
});
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<Grid>
<Paper elevation={10} style={paperStyle}>
<Grid align='center'>
<Avatar style={avatarStyle}><LockIcon/></Avatar>
<h2>Sign In</h2>
</Grid>
<TextField label='Username' placeholder='Enter username' fullWidth required onChange={e => setEmail(e.target.value)}/>
<TextField label='Password' placeholder='Enter password' type='password' fullWidth required onChange={e => setPassword(e.target.value)}/>
<FormControlLabel
control={
<Checkbox
name="checkedB"
color="primary"
/>
}
label="Remember me"
/>
<Button type='submit' disabled={!validateForm()} color='primary' variant="contained" style={btnstyle} fullWidth>Sign in</Button>
<Typography >
<Link to="#" >
Forgot password ?
</Link>
</Typography>
<Typography > Do you have an account ?
<Link to="/AddAccount" >
Sign Up
</Link>
</Typography>
</Paper>
</Grid>
</form>
)
}
After hours of looking I've finally found the most up to date tool you are meant to use for react router in the latest update (currently 6.1.1)
This is about 3 posts down in this post (the other answers are all for older versions of react router and will not work)
The answer is simple:
import { useNavigate } from "react-router-dom";
then in your function add a simpler variable for useNavigate:
let navigate = useNavigate();
and replace your window.location.assign("/Home") with navigate("/Home", { replace: true });
(obviously could also just say useNavigate("/Home", {replace:true})

React Router 4 can not load new content on same component with <Link>

I can't seem to trigger any other react component life cycle method other than render() when I click on a link that leads to a page that loads exactly the same component, even though the url is different. So here's my code
//index.js - the entry point
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom';
import Config from './Settings/Config';
import App from './Components/App';
const c = new Config();
render(
<BrowserRouter basename={c.routerBaseName}>
<App />
</BrowserRouter>
, document.getElementById('root'));
Here's my App JS
// Components/App.js
import React, {Component} from 'react';
import {Route} from 'react-router-dom';
import BlogEntry from './BlogEntry';
export default class App extends Component {
render() {
console.log('app');
return (
<div>
<Route exact path="/blog/:name" component={BlogEntry} />
</div>
)
}
}
And here is my BlogEntry.js
// Components/BlogEntry.js
import React from 'react';
import { Link } from 'react-router-dom';
export default class BlogEntry extends React.Component {
async componentDidMount() {
const [r1] = await Promise.all([
fetch(`http://api.myservice.com/${this.props.match.params.name}`)
]);
this.setState({content:await r1.json()});
console.log('fetch');
}
render() {
console.log('render');
if(!this.state) return <div></div>;
if(!this.state.content) return <div></div>;
const content = this.state.content;
return (
<div id="blog-entry" className="container">
<h1>{content.title}</h1>
<div dangerouslySetInnerHTML={{__html:content.content}}></div>
<div className="related-other">
<h2>Related Content</h2>
<ul>
<li><Link to="/blog/new-york-wins-the-contest">New York Wins the Contest!</Link></li>
<li><Link to="/blog/toronto-with-some-tasty-burgers">Toronto with Some Tasty Burgers</Link></li>
</ul>
</div>
</div>
);
}
}
So what happens is that when I click on the link for Toronto with Some Tasty Burgers or New York Wins the Contest! I see the url in my web browser address bar update accordingly. But my componentDidMount does not fire. And hence no new content is fetched or loaded.
React also won't let me put an onPress event handler to the <Link> object. And even if I did, managing the history state when browser clicks back button would be a nightmare if I were to create my own onpress event handler to load pages.
So my question is, how do I make it so that clicking on one of the links actually causes the component to fetch new data and redraw and also be part of the browser back button history?
I added this to my BlogEntry.js and everything works now:
componentWillReceiveProps(nextProps) {
this.props = nextProps;
}
I don't think your proposed solution, via componentWillReceiveProps (deprecated) is good enough. It's a hack.
Why don't you keep the route id in the state (as in /blog/:id).
Then something like this:
componentDidUpdate() {
const { match: { params: { id: postId } = {} } } = this.props;
if(this.state.postId !== postId) {
// fetch content
}
}

react-router 4 - Browser history needs a DOM

I am trying server side rendering using react-router 4. I am following the example provided here https://reacttraining.com/react-router/web/guides/server-rendering/putting-it-all-together
As per the example on server we should use StaticRouter. When I import as per the example I am seeing StaticRouter as undefined
import {StaticRouter} from 'react-router';
After doing some research online I found I could use react-router-dom. Now my import statement looks like this.
import {StaticRouter} from 'react-router-dom';
However when I run the code I am getting Invariant Violation: Browser history needs a DOM in the browser.
my server.js file code
....
app.get( '*', ( req, res ) => {
const html = fs.readFileSync(path.resolve(__dirname, '../index.html')).toString();
const context = {};
const markup = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context} >
<App/>
</StaticRouter>
);
if (context.url) {
res.writeHead(302, {
Location: context.url
})
res.end();
} else {
res.send(html.replace('$react', markup));
}
} );
....
And my client/index.js code
....
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>
), root);
....
Update v1
Reduced my example to a bear minimum and still getting the same error.
clientIndex.js
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from '../App'
ReactDOM.render((
<BrowserRouter>
<App/>
</BrowserRouter>
), document.getElementById('app'))
serverIndex.js
import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from '../App'
createServer((req, res) => {
const context = {}
const html = ReactDOMServer.renderToString(
<StaticRouter
location={req.url}
context={context}
>
<App/>
</StaticRouter>
)
res.write(`
<!doctype html>
<div id="app">${html}</div>
`)
res.end()
}).listen(3000);
App.js
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import routes from "./client/routes";
const App = ( ) => (
<Router>
<Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
</Router>
)
export default App;
You need to use different history provider for server side rendering because you don't have a real DOM (and browser's history) on server. So replacing BrowserRouter with Router and an alternate history provider in your app.js can resolve the issue. Also you don't have to use two wrappers. You are using BrowserRouter twice, in app.js as well as clientIndex.js which is unnecessary.
import { Route, Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
const history = createMemoryHistory();
<Router history={history}>
<Route path="/" exact render={( props ) => ( <div>Helloworld</div> )} />
</Router>
You can now replace StaticRouter with ConnectedRouter which can be used both in client and server. I use the following code to choose between history and export it to be used in ConnectedRouter's history.
export default (url = '/') => {
// Create a history depending on the environment
const history = isServer
? createMemoryHistory({
initialEntries: [url]
})
: createBrowserHistory();
}
In clientIndex.js
Rather than BrowserRouter use StaticRouter.
import { BrowserRouter } from 'react-router-dom';
import { StaticRouter } from 'react-router-dom'
As is essentially noted in the comments, one may hit this error (as I have) by accidentally wrapping your App component in a <BrowserRouter>, when instead it is your client app that should be wrapped.
App.js
import React from 'react'
const App = () => <h1>Hello, World.</h1>
export default App
ClientApp.js
import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import ReactDOM from 'react-dom'
import App from './App'
const render = Component => {
ReactDOM.render(
<BrowserRouter>
<Component />
</BrowserRouter>,
document.getElementById('app')
)
}
render(App)
See also the React Router docs.

Router ignores this.props.children when routes are imported but not when inline

Introduction
I am attempting to create an Isomorphic ReactJS application using React-Router and ExpressJS. This requires some modularity so both client and server has access to specific scripts such as the routes. The modularity is achieved through Browserify and the router guides I have followed are react-router lesson server rendering and react-router guide server rendering.
Problem
The router behaves differently when routes are imported from an external routes.jsx as opposed to being written in the same file as the router. The this.props.children is ignored when the routes are imported and the view replaces the whole app, while is honored when in the routes are in the same file and replaces only the subsection of the app (desired outcome).
Example
routes.jsx - For both server and client
import React from 'react';
import {Route, IndexRoute, Redirect} from 'react-router';
/* Base */
import App from './client.jsx';
/* Views */
import HomeView from '_view/HomeView';
import WorkView from '_view/WorkView';
const Routes = (
<Route path="/" component={App}>
<IndexRoute component={HomeView} />
<Redirect from="home" to="/" component={HomeView} />
<Route path="work" component={WorkView} />
</Route>
);
export default Routes;
client.jsx - Imported routes
When using imported Routes the whole of <main> is replaced with eg. HomeView rather than just {this.props.children}.
import React from 'react';
import {render} from 'react-dom';
import {Router, browserHistory} from 'react-router';
/* Routes */
import Routes from './routes.jsx';
/* Components */
import Header from '_component/Header';
/* Views */
import HomeView from '_view/HomeView';
import WorkView from '_view/WorkView';
export default class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<main>
<Header />
{this.props.children}
</main>
)
}
}
if(typeof window !== 'undefined' && typeof document !== 'undefined') {
render(<Router history={browserHistory} routes={Routes} />,
document.getElementById('root'));
}
client.jsx - Inline routes
When using inline Routes only {this.props.children} is replaced with eg. HomeView. However this removes the modularity and the server can no longer reach the routes. This means either export the routes from client.jsx or have two copies of the same rules. Neither of which I find appealing.
import React from 'react';
import {render} from 'react-dom';
import {Router, Route, IndexRoute, Redirect, browserHistory} from 'react-router';
/* Components */
import Header from '_component/Header';
/* Views */
import HomeView from '_view/HomeView';
import WorkView from '_view/WorkView';
export default class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<main>
<Header />
{this.props.children}
</main>
)
}
}
const Routes = (
<Route path="/" component={App}>
<IndexRoute component={HomeView} />
<Redirect from="home" to="/" component={HomeView} />
<Route path="work" component={WorkView} />
</Route>
);
if(typeof window !== 'undefined' && typeof document !== 'undefined') {
render(<Router history={browserHistory} routes={Routes} />,
document.getElementById('root'));
}
Question
Where does the error lie? With my code, with react-router, or with browserify's export method?
Turns out my inexperience with browserify caused this issue. As always with code it can be hard to debug what goes wrong but the answer was simple:
There is a circular reference,maybe here is the problem – gu mingfeng
Thank you, stackoverflow!

Hello SO, i just changed a React with MartyJS flux from SSR to Client side rendering, having issues with react-router

So as the title describes im doing an app available on github at denlillemand/react. It has a express backend that just serves a simple html template that imports my bundle.js. And the app initially renders just fine, but none of the routes seem to work.
Below is the components that are involved:
./Application.js
import React from "react";
import Marty from "marty";
import actions from "./actions";
import stores from "./stores";
import queries from "./queries";
import sources from "./sources";
import Router from "./Router";
import RealApp from "./components/App";
class Application extends Marty.Application {
constructor(options) {
super(options);
this.register(actions);
this.register(stores);
this.register(queries);
this.register(sources);
this.router = Router;
}
}
var app = new Application();
var { ApplicationContainer } = require('marty');
app.router.run(function(Handler,state){
React.render((
<ApplicationContainer app={app}>
<Handler{...state} />
</ApplicationContainer>
), document.getElementById("app"));
});
./components/App.js
import React from "react";
import Router from "react-router";
import Header from "./core/Header";
import Footer from "./core/Footer";
var RouteHandler = Router.RouteHandler;
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div>
<Header />
<RouteHandler />
<Footer />
</div>
);
}
}
./Routes.js
import React from "react";
import {Route} from "react-router";
import {DefaultRoute} from "react-router";
import App from "./components/App";
import About from "./components/About";
import ChapterOne from "./components/chapterone/ChapterOne";
import ChapterTwo from "./components/chaptertwo/ChapterTwo";
import ChapterThree from "./components/chapterthree/ChapterThree";
import ChapterFour from "./components/chapterfour/ChapterFour";
export default (
<Route name="app" path="/" handler={App}>
<Route path="/about" handler={About}/>
<Route path="/chapters/chapterone" handler={ChapterOne} />
<Route path="/chapters/chaptertwo" handler={ChapterTwo} />
<Route path="/chapters/chapterthree" handler={ChapterThree} />
<Route path="/chapters/chapterfour" handler={ChapterFour} />
<DefaultRoute name="default" handler={About}/>
</Route>
);
./Router.js
import Router from "react-router";
import routes from "./Routes";
let location = () => {
if(typeof window !== undefined){
return Router.historyLocation;
}
}
export default Router.create({
routes:routes,
location:location()
});
The default route seems to be the only one that works, since im hitting the About component. This app worked when it was SSR not long ago, and im kind of wondering if ive setup react-router correctly, but looking at the DOCS it seem like i have.
i guess i have to answer my own question,
The creator of MartyJS recently killed off his own project,
if you follow his twitter he announced it ..
so i guess im moving to redux since it has like 3500+ github starts i suppose it will be easier to find support for redux issues that arise .
Thanks .