I'm trying to use React Router in my react app which is bounded as wordpress plugin and uses flux to fetch api data.
my entry point looks as it follows
import React from 'react';
import Workshops from './components/workshops';
import Workshop from './components/workshop';
import NotFound from './components/notfound';
import Router, { Route, DefaultRoute, NotFoundRoute, Redirect, Link } from 'react-router';
import json from './config.json';
localStorage.clear();
localStorage.setItem('workshops', JSON.stringify(json));
const AppRoutes = (
<Route path="/" handler={Workshops}>
<DefaultRoute handler={Workshop} />
<Route name="workshop" path=":slug" handler={Workshop}/>
<NotFoundRoute handler={NotFound} />
</Route>
);
Router.run(AppRoutes, Router.HashLocation, (Root) => {
React.render(<Root />, document.getElementById('workshop-booker'));
});
than in my Workshops component I make some links to a given route, I have hash changes but the routed component does not getting fired.
<h3> <Link to="workshop" params={{slug: workshop.slug }}> {workshop.title.rendered }</Link></h3>
You can wrap your Router with a DebugRouter which will print the navigation actions made:
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Login from 'components/Login'
import DefaultComponent from 'components/DefaultComponent'
class DebugRouter extends BrowserRouter {
constructor(props){
super(props);
console.log('initial history is: ', JSON.stringify(this.history, null,2))
this.history.listen((location, action)=>{
console.log(
`The current URL is ${location.pathname}${location.search}${location.hash}`
)
console.log(`The last navigation action was ${action}`, JSON.stringify(this.history, null,2));
});
}
}
class App extends Component {
render() {
return (
<DebugRouter>
<Switch>
<Route exact path="/login" name="Login Page" component={Login} />
<Route path="/" name="Home" component={DefaultComponent} />
</Switch>
</DebugRouter>
);
}
}
link to the gist
I made my DebugRouter for functional components
const DebugRouter = ({ children }: { children: any }) => {
const { location } = useHistory()
if (process.env.NODE_ENV === 'development') {
console.log(
`Route: ${location.pathname}${location.search}, State: ${JSON.stringify(location.state)}`,
)
}
return children
}
const Router = () => {
return (
<BrowserRouter>
<Layout>
<Route
render={() => {
return (
<DebugRouter>
<Switch>
<Redirect exact from="/" to={...} />
// <Route/> should be here
<Redirect from="*" to={...} />
</Switch>
</DebugRouter>
)
}}
/>
</Layout>
</BrowserRouter>
)
}
You can try something like this
import React, {Component} from 'react';
import PropTypes from 'prop-types'
import {withRouter} from "react-router-dom";
class RouterDebugger extends Component {
componentWillUpdate(nextProps, nextState){
console.log('componentWillUpdate',nextProps, nextState)
}
componentDidUpdate(prevProps) {
console.log('componentDidUpdate',prevProps)
}
render() {
return null
}
}
export default withRouter(RouterDebugger)
And insert this component in any place you want to debug.
You can pass a prop with some identifier
i hope this help you
You can use the following code to debug React Router:
console.log(this.props.location)
console.log(this.props.match)
Related
I'm trying to import /app/javascript/components/Videochat.js into app/javascript/packs/index.js but getting the error module not found Can't resolve Videochat ...
index.js:
import React from 'react'
import ReactDom from 'react-dom'
import Videochat from '../components/Videochat'
document.addEventListener('DOMContentLoaded', () => {
ReactDom.render(
<Videochat/>,
document.body.appendChild(document.createElement('div')),
)
})
Videochat.js
import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from "react-router-dom"
import CreateRoom from "./routes/CreateRoom"
import Room from "./routes/Room"
class Videochat extends React.Component {
render () {
return (
<BrowserRouter>
<Switch>
<Route path="/" exact component={CreateRoom} />
<Route path="/room/:roomID" component={Room} />
</Switch>
</BrowserRouter>
)
}
}
export default Videochat
I solved this by changing the component name from Videochat to App
It seems the presence of App.js in the components folder is of great significance In my case I was importing the Videochat component but not the App component even though it was present
the code below is one of my component.
i am creating this with Ruby on Rails framework, with react_rails gem and webpacker, experimenting on Material UI.
as you can see, i am changing the Material UI default font theme with my own choice of font. below code is a success.
my question is, do i have to repeat this step for all my component?
importing createMuiTheme, stating the theme const, and wrapping <MuiThemeProvider /> in every render?
is there a single way to do this universally, without repeating in all component?
thanks for the advice.
import React from 'react';
import PropTypes from 'prop-types';
import Card from '#material-ui/core/Card';
import CardActions from '#material-ui/core/CardActions';
import CardContent from '#material-ui/core/CardContent';
import Button from '#material-ui/core/Button';
import Popover from '#material-ui/core/Popover';
import Typography from '#material-ui/core/Typography';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import Avatar from '#material-ui/core/Avatar';
import EmailIcon from '#material-ui/icons/Email';
import HomeIcon from '#material-ui/icons/Home';
import PersonIcon from '#material-ui/icons/Person';
import { MuiThemeProvider, createMuiTheme, withStyles } from '#material-ui/core/styles';
const theme = createMuiTheme({
typography: {
fontFamily: 'Bebas',
},
});
export class SimpleCard extends React.Component {
render () {
return (
<div >
<MuiThemeProvider theme={theme}>
<Card raised="true">
<CardContent >
<List>
<ListItem>
<Avatar>
<EmailIcon />
</Avatar>
<ListItemText primary="Email" secondary={this.props.order.order_mail} />
</ListItem>
</List>
</CardContent>
</Card>
</MuiThemeProvider>
</div>
);
}
}
export default withStyles(styles)(SimpleCard);
Did you try wrapping the MuiThemeProvider around the entire site/app? This is what I do in React.js. I set up my theme in the root file and wrap it around the entire component
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
// Components
import Navbar from "./components/layout/Navbar";
import Footer from "./components/layout/Footer";
import Login from "./components/auth/Login";
import Dashboard from "./components/dashboard/Dashboard";
// Styles
import "./stylesheets/App.css";
import {
MuiThemeProvider,
createMuiTheme,
withTheme
} from "#material-ui/core/styles";
import { grey } from "#material-ui/core/colors";
import { withStyles } from "#material-ui/core";
const theme = createMuiTheme({
overrides: {
MuiGrid: {
container: {
width: "100%",
margin: "0"
}
}
},
palette: {
primary: {
light: "#c146b1",
main: "#8e0081",
dark: "#5c0054",
contrastText: "#ffffff"
},
secondary: {
light: "#6bffff",
main: "#00eae3",
dark: "#00b7b1",
contrastText: "#000000"
}
}
});
const drawerWidth = 240;
const styles = theme => ({
app: {
backgroundColor: grey[200]
},
drawerOpen: {
marginLeft: 0
},
drawerClosed: {
marginLeft: -drawerWidth
}
});
class App extends Component {
constructor() {
super();
this.state = {
navOpen: false
};
}
toggleDrawer = () => {
this.setState({
navOpen: !this.state.navOpen
});
};
render() {
const { classes } = this.props;
return (
<MuiThemeProvider theme={theme}>
<div className={classes.app}>
<Navbar
toggleDrawer={this.toggleDrawer}
navOpen={this.state.navOpen}
/>
<Route exact path="/" component={Dashboard} />
<Route exact path="/register" component={PatientRegister} />
<Route exact path="/login" component={Login} />
<Footer />
</div>
</Router>
</MuiThemeProvider>
);
}
}
export default withTheme(theme)(withStyles(styles)(App));
This is an example of my component that will be rendered in the root div (aka the entire application). Notice how wraps the entire app? I stripped a lot out to make it simpler to understand, but if you are using Redux (which is awesome) then I would recommend having that as your outer wrapper, and the rest inside of that. In other words:
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<div class="App">
// Your App Here
</div>
</MuiThemeProvider>
</Provider>
I have used React native native base library for Tabbar with 4 screens but it is flickering while switch tabs.
import React, { Component } from 'react';
import { Container, Header, Content, Tab, Tabs } from 'native-base';
import Tab1 from './tabOne';
import Tab2 from './tabTwo';
export default class TabsExample extends Component {
render() {
return (
<Container>
<Header hasTabs />
<Tabs initialPage={1}>
<Tab heading="Tab1">
<Tab1 />
</Tab>
<Tab heading="Tab2">
<Tab2 />
</Tab>
<Tab heading="Tab3">
<Tab3 />
</Tab>
</Tabs>
</Container>
);
}
}
I had a similar problem when I developed an app as well in react-native. The problem for me was that I had used componentsWillUpdate for animations. Instead I did a helper function for the animations.
Don't know how the rest of your code looks like but this solved my problem.
You can be here that you want to
1, Install: switch-react-native
npm i switch-react-native
2, Using lib:
import React, { Component } from 'react';
import { View } from 'react-native';
import { Switch } from 'switch-react-native';
class SwitchExample extends Component {
render() {
return (
<View>
<Switch
height={40}
width={300}
activeText={`Active Text`}
inActiveText={`InActive Text`}
onValueChange={(value: any) => console.log(value)}
/>
</View>
);
}
}
I'm new to ReactJS and I followed this tutorial: http://adamalbrecht.com/2015/07/20/authentication-using-json-web-tokens-using-rails-and-react/
After I make some corrections to the code to get it working on my setup, I can successfully login. But when I reload the page the SessionStore is empty.
My router:
import React from 'react';
import { Provider } from 'react-redux'
import { Router, Route, IndexRoute, hashHistory } from 'react-router';
// Base-Layout
import Base from './layout/base';
// Pages
import index from './pages/index';
import Login from './pages/Login';
import SessionStore from './stores/session_store.js';
function requireAuth(store) {
return (nextState, replace) => {
// let { auth } = store.getState();
console.log(store.isLoggedIn());
// if (!auth || !auth.loggedIn)
// replace({ pathname: loginPath, query: { return_to: nextState.location.pathname } });
};
}
export default (
<Provider store={SessionStore}>
<Router history={hashHistory}>
<Route path="login" name="Login" component={Login} />
<Route path="/" name="Home" component={Base} onEnter={requireAuth(SessionStore)}>
<IndexRoute component={index}/>
</Route>
</Router>
</Provider>
);
When I go to index page after successfull login I get a "true" but when I reload the page I got an "false" maybe anyone can help me.
You should use localStorage instead of sessionStorage. This is because sessionStorage is cleared every time page session ends, e.g. page is refreshed.
I have followed the example here to try and create a basic authenticated area
for my app, which is a solution I really like in principle. Here is my index.js:
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRedirect to="authenticated" />
<Route path="setup" component={SetupJourney} >
<Route path="details" component={Details}/>
<Route path="account" component={AccountType}/>
</Route>
<Route path="authenticated" component={requireAuthentication(secretPage)} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
);
and then here's my AuthenticatedComponent higher-order component to handle redirects:
export function requireAuthentication(Component) {
class AuthenticatedComponent extends React.Component {
componentWillMount() {
this.checkAuth();
}
componentWillReceiveProps(nextProps) {
this.checkAuth();
}
checkAuth() {
if (!this.props.isAuthenticated) {
this.props.dispatch(pushState(null, '/setup/details', ''));
}
}
render() {
return (
<div>
{this.props.isAuthenticated === true
? <Component {...this.props}/>
: null
}
</div>
)
}
}
const mapStateToProps = (state) => ({
isAuthenticated: state.get('user').get('isAuthenticated')
});
return connect(mapStateToProps)(AuthenticatedComponent);
}
I've been playing about with this for ages and I cannot get the component to redirect. In Redux dev tools I can see the ##reduxReactRouter/historyAPI action has fired, but the URL doesn't change. All the relevant props/state etc seem to be in place too...is there something I've missed?
Thanks
For anyone coming across this, I eventually resolved this and there were a few problems. Firstly, pushState is only for use with browserHistory, so I needed to switch from hashHistory.
After this the pushState still did not work. This can apparently happen when middleware is specified in the wrong order. I restructured my index.js to follow the pattern in Redux's real world example, and everything eventually worked.
The key bit is I now have a store.js file that looks like this:
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { routerReducer, routerMiddleware} from 'react-router-redux';
import { browserHistory } from 'react-router';
import reducer, { INITIAL_STATE } from '../reducers/reducer';
const rootReducer = combineReducers({reducer, routing: routerReducer});
const composeEnhancers =
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(routerMiddleware(browserHistory))
);
const initialState = {'reducer': INITIAL_STATE};
export default function configureStore() {
return createStore(rootReducer, initialState, enhancer);
}