I am building a react application and the react router renders a black page. I've googled around and can't seem to figure out what's going on.
index
import React from 'react'
import {render} from 'react-dom'
import routes from './routes'
render(routes, document.getElementById('root'))
routes
import React, { Component } from 'react'
import { Router, Route, hashHistory } from 'react-router'
import Home from './Home.js'
import Name from './Name.js'
//import level from './level.js'
//import level1 from './level1.js'
//import level2 from './level2.js'
//import result from './result.js'
const routes = (
<Router history={hashHistory}>
<Route path='/' component={Home} />
<Route path='/name' component={Name} />
</Router>
);
export default routes;
component that doesn't render by navigating /name
import React, { Component } from 'react'
import appState from './state.js'
import { Router } from 'react-router'
class Name extends Component {
constructor() {
super();
this.state = {username: ''};
}
onUpdateUser = (e) => {
this.appState.username = e.target.value;
Router.push({
pathname: '/level'
})
}
render() {
return (
<div className="row">
<div claassName="col-md-12">
<div className="nameBox">
<form className="form-inline" onSubmit={this.onUpdateUser()}>
<input type="text" className="form-control" placeholder="Desiered Username" onChange={this.onUpdateUser} value={this.state.username} />
<button type="submit" className="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
)
}
}
export default Name
Any help would be appreciated!PS The index route works fine.
What's the path of ./routes? Do you have a /routes/index.js file that consists of the code that you put for the routes?
Also I recommend that you use browserHistory instead of hashHistory for 'normal' url's, without hashes. More info about that here
For your Name class I would recommend you to use the withRouter Higher Order Component from React Router. This injects 'router' as a prop, inside of your component, so you can use this.props.router.push('/path').
this.appState actually does nothing right now. You're importing 'appState' that isn't being touched. Right now you're setting 'appState' within the Name component. Don't you mean to use this.setState({ username: e.target.value })?. It's also a better practice to use onUpdateUser(e) { code } instead of arrow functions for a class function.
I also see <form className="form-inline" onSubmit={this.onUpdateUser()}> - I think that onUpdateUser is currently called when rendering this component. You should do onSubmit={this.onUpdateUser} instead, so the function gets called when onSubmit is triggered. Or onSubmit={e => this.onUpdateUser(e)}, both things work.
What exactly are you trying to achieve with this code?
Edit:
I've added a gist in which I created the 'introduction - set username - choose level - rest' flow without using React Router. React Router isn't always necessary, especially for things in which you really want to control the flow of the views that have to be shown.
https://gist.github.com/Alserda/150e6784f96836540b72563c2bf331d0
Add this to your index file. You might need to use the actual Router from react-router.
//Import Dependencies.
import { Router } from 'react-router';
//Import Routes.
import routes from './routes/routes.js';
render(<Router routes={routes} />, document.getElementById('root'))
OR
Use ReactDom for react-dom instead of render.
import ReactDOM from 'react-dom';
ReactDom.render(<Router routes={routes} />, document.getElementById('root'));
To use this with current versions of react-router (v4) you need to use a switch element.
Related
So, I'm doing some assignment and I get the following error when running my react app (Should be a dynamic URL like /item/n):
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
at Routes (http://localhost:3000/static/js/bundle.js:35605:5)
at Router (http://localhost:3000/static/js/bundle.js:35538:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:35014:5)
index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import {
BrowserRouter as Router,
Routes,
Route
} from "react-router-dom";
import ProductMainView from './components/productMainView'
const rootElement = document.getElementById('root')
ReactDOM.render(
<Router>
<Routes>
<Route exact path="/item/:item_id"
element={(props) => <ProductMainView id={props.params.id} />} />
</Routes>
</Router>,
rootElement
);
productMainView.jsx:
import React, { Component } from 'react'
class ProductMainView extends Component {
componentDidMount() {
console.log(this.props)
let id = this.props.match.params.item_id
}
render() {
return <h1>Placeholder</h1>
}
}
export default ProductMainView
Yes, I know that class-based component are kinda obsolete, but I'm limited by the assignment.
Note: If the method of getting the id to the component is wrong, other ways would be welcome.
Note v2: I'm usign react-router v6.2.2
First off, I have read through just about every example I can find and looked through various boilerplates to see how others have done this. I am having issues loading pages when clicking <Link>'s with react-router v4. I have also installed the package react-router-connected and have been trying that out as well but no improvement can be seen (however it shows the changes in the redux-logger which is nice).
Currently, the url updates just fine and if I manually change the url and press enter, then the next page will load. But, it will not redirect if I click a link. I am also using create-react app for the project, just for your reference. My actual app is setup as the exact example from usage with react-router in the official redux docs. For simplicity, I have changed my routes to only include links to basic components that do nothing but redirect to one another.
Root.js which houses my routes
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router'
// import App from './App';
import NewComponent from './NewComponent';
import OldComponentent from './OldComponent';
const Root = ({ store, history }) => (
<Provider store={store}>
<ConnectedRouter history={history}>
<Router>
<Switch>
<Route exact path='/' component={OldComponentent}/>
<Route path='/new' component={NewComponent}/>
{/* <Route path='/' component={App}/>
<Route path='/:filter' component={App}/> */}
</Switch>
</Router>
</ConnectedRouter>
</Provider>
)
export default Root;
Home component
import React from 'react';
import { connect } from 'react-redux';
import { push } from 'connected-react-router';
import { withRouter } from 'react-router';
import { Link } from 'react-router-dom';
import Button from 'material-ui/Button';
class OldComponent extends React.Component {
redirectPage = () => { this.props.dispatch(push('/new')); };
redirectPage1 = () => { this.props.dispatch(push('/')); };
render() {
return (
<div>
OLD COMPONENT
<Button onClick={this.redirectPage}>Redirect new</Button>
<Button onClick={this.redirectPage1}>Redirect /</Button>
<Link to='/new'>Redirect Link</Link>
</div>
)
}
}
export default withRouter(connect()(OldComponent));
Other basic component for redirection purposes
import React from 'react';
import { connect } from 'react-redux';
import { push } from 'connected-react-router';
import { withRouter } from 'react-router';
import { Link } from 'react-router-dom';
import Button from 'material-ui/Button';
class NewComponent extends React.Component {
redirectPage = () => { this.props.dispatch(push('/')); };
redirectPage1 = () => { this.props.dispatch(push('/new')); };
render() {
return (
<div>
NEW COMPONENT
<Button onClick={this.redirectPage}>Redirect /</Button>
<Button onClick={this.redirectPage1}>Redirect new</Button>
<Link to='/new'>Redirect Link</Link>
</div>
)
}
}
export default withRouter(connect()(NewComponent));
As you can see, they are essentially the same component with minor differences. The url will change to /new or / and will also update the pathname found in the ##router/LOCATION-CHANGE state objects created by react-router-connected package. The url will also change by clicking the <Link> tag but with no redirect.
Any help on how to approach this would be greatly appreciated.
The comment posted by #Supertopoz works this.props.history.push('/pathname') works. However, after setting that up, the <Link> now works as well. I am also using withRouter (which I was before) throughout, so that was another important factor in egtting it to work.
I have server side React/Redux/Express app.
React-router v4 provides solution for a server app with Switch and I need to use something to change location from my NavBar component
App
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { Switch, Route, Redirect, Link} from 'react-router-dom'
import FirstPage from './FirstPage'
import Test from './Test'
import LoginPage from './login/LoginPage'
import NoMatch from '../components/NoMatch'
import NavBar from '../components/NavBar'
import * as loginActions from '../actions/login'
import 'bootstrap/dist/css/bootstrap.css';
class App extends Component {
render(){
return (
<div>
<NavBar/>
<h1>EffortTracker v3.0.1</h1>
<Switch >
<Route exact path="/" render={loginRedirect(<FirstPage/>)}/>
<Route path="/first" render={loginRedirect(<FirstPage/>)}/>
<Route path="/second" render={loginRedirect(<Test/>)}/> />
<Route path="/login" render={()=><LoginPage {...this.props.login}/>} />
<Route component={NoMatch}/>
</Switch>
<Link to={'/first'}>First</Link>
</div>
)
}
}
const loginRedirect=(component) => {
if(!isLoggedIn()) return ()=> <Redirect to='login'/>
return ()=> component
}
const isLoggedIn= ()=>{
let token = localStorage.getItem('token')
if (token !== null)return false
else return true
}
const mapStateToProps = state => ({
login: state.login,
error: state.error,
isLoading: state.isLoading,
})
const mapDispatchToProps = dispatch => ({
loginActions: bindActionCreators(loginActions, dispatch)
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
Need to change from
NavBar
import React from 'react'
import { Link, NavLink } from 'react-router-dom'
import classnames from 'classnames'
const NavBar =()=> {
return (
<nav className={classnames("navbar", "navbar-inverse bg-inverse")}>
<form className="form-inline">
<Link to={'/'}>
<button className={classnames("btn", "btn-sm", "align-middle", "btn-outline-secondary")}
type="button">
Smaller button
</button>
</Link>
<NavLink to='/login'>
Login
</NavLink>
</form>
</nav>
)
}
export default NavBar
If I navigate it manually from browser url its work just fine but if I click a Link or NavLink url is updated but not the App Switch. Also I have an issue when loginRedirect to /login it does not appear and need to refresh page (possible that this two is related ).
How to fix this?
I think the problem here is with redux .. because it blocks rerendering the components as long as the props didn't change,
This is because connect() implements shouldComponentUpdate by default, assuming that your component will produce the same results given the same props and state.
The best solution to this is to make sure that your components are pure and pass any external state to them via props. This will ensure that your views do not re-render unless they actually need to re-render and will greatly speed up your application.
If that’s not practical for whatever reason (for example, if you’re using a library that depends heavily on React context), you may pass the pure: false option to connect():
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps, null, null, {
pure: false
})(TodoApp)
here are links for more explanation:
react-redux troubleshooting section
react-router DOCS
If using Redux, the redux connect HOC overrides the shouldComponentUpdate lifecycle method on your component and checks for props and state change this can confuse the React Router. Something like a user clicking a link will not necessarily change the state or props as is, leading to not re-rendering the components in the routeing context.
The documentation for react router states a solution for this problem:
Wrap the component with the withRouter HOC
import { Route, Switch, withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
const Main = (props) => (
<main>
<Switch>
<Route exact path='/' component={SAMPLE_HOME}/>
<Route path='/dashboard' component={SAMPLE_DASHBOARD}/>
</Switch>
</main>
)
export default withRouter(connect()(Main))
Also, as an enclosing route component will pass props with a location property, you can pass that into the child component and that should achieve the desired behaviour.
https://reacttraining.com/react-router/web/guides/dealing-with-update-blocking
I am having some trouble with a component not rendering after following its route. The routes are created in a parent component Drawings, and send a couple of props to Drawing components.
When I click the link, I get to the correct path, for example, /drawing/20170724, and the log statement I have in the render function runs. I also get the props, so far so good. However, the return doesn't happen, so the HTML I need isn't available.
Here is Drawings where the routes and links are created:
import React, { Component } from "react";
import { Route, Link } from "react-router-dom";
import AnimatedWrapper from "../modules/AnimatedWrapper";
import Drawing from '../components/Drawing';
class DrawingsComponent extends Component {
render() {
const drawings = this.props.drawings;
const drawingsMap = this.props.drawingsMap;
return(
<div>
<div className="page">
<div className="drawings-list">
{
drawings.map((drawing) => {
return(
<Link to={`/drawing/${drawing.date}`} key={drawing.date}>
<div className="drawing-thumb">
<h2>{drawing.date}</h2>
</div>
</Link>
)
})
}
</div>
{
Object.keys(drawingsMap).map((d, i) => {
return <Route path={`/drawing/${drawings[i].date}`} render={(props) => (<Drawing drawingPkg={drawingsMap[d]} drawingInfo={drawings[i]} {...props} />)} key={`${drawings[i].date}`}/>
})}
</div>
</div>
)
}
}
const Drawings = AnimatedWrapper(DrawingsComponent);
export default Drawings;
And here is Drawing:
import React, { Component } from 'react';
import AnimatedWrapper from "../modules/AnimatedWrapper";
class DrawingComponent extends Component {
render() {
const drawing = this.props.drawingPkg;
const drawingInfo = this.props.drawingInfo;
console.log('gonna draw now');
return(
<div className="drawing">
<h2 className="drawing-title">{drawingInfo.title}</h2>
<canvas></canvas>
</div>
)
}
}
const Drawing = AnimatedWrapper(DrawingComponent);
export default Drawing;
I can't figure out why the Drawing component isn't returning.
What you are doing is wrong. You change the URL when the link is clicked. Then you have to give routes configuration, that tells react router which component to render when the URL changes. It should be something like this inside your index.js file.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import promise from 'redux-promise';
import reducers from './reducers';
import Drawing './components/drawing_component';
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<div>
<Switch>
<Route path="/drawings/:date" component={Drawing} />
<Route path="/" component={IndexComponent} />
</Switch>
</div>
</BrowserRouter>
</Provider>
, document.querySelector('.container'));
I think what is wrong here is your routes config. I have never seen route config is given like the way you do.
Then write a separate Drawing component which renders a given drawing instance.
With this change your Link should be like this.
<Link to={`/drawing/${drawing.date}`}>
{drawing.date}
</Link>
The bottom line is this. Here you give some text with a hyperlink. Upon clicking this router changes the URL as in your case now. Then it uses router config to render the relevant component.
I need to navigate to a route after an event is successful.
This seems to have changed since previous versions.
Previously we would do this:
import { browserHistory } from 'react-router';
...
handleClick(){
doSomething();
browserHistory.push('/some/path');
}
This example works in react-router and react-router-dom v4.0.0.
We have 3 components:
App.js - holds both Component 1 and Component 2
Component1.js - not wrapped in a Route and will always be rendered, but this will not have a reference of the "route props" - (history, location, match...)
Component2.js - rendered only if the route location match. Important thing to note that this component will be rendered with "route props"
To navigate programmatically, you can use react-router history object.
this.props.history.push('path');
This will work right off the bat for components rendered via Route, as these components will already have access to the route props (history). In our example this is Component2. However, for components that are not rendered via a Route (e.g. Component1), you would need to wrap it in withRouter to give you access to the history object.
App.js
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Component1 from './Component1';
import Component2 from './Component2';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Component1 />
<Route path="/render1" render={() => <div>Rendered from Component1</div>} />
<Route path="/" component={Component2} />
<Route path="/render2" render={() => <div>Rendered from Component2</div>} />
</div>
</BrowserRouter>
)
}
}
export default App;
Component1.js
import React from 'react';
import { withRouter } from 'react-router';
class Component1 extends React.Component {
handleButtonClick() {
this.props.history.push('/render1');
}
render() {
return (
<div>
<h1>Component 1</h1>
<button onClick={this.handleButtonClick.bind(this)}>Component 1 Button</button>
</div>
);
}
}
const Component1WithRouter = withRouter(Component1);
export default Component1WithRouter;
For Component1, we wrapped it in withRouter, and then exported the returned wrapped object. Some gotcha, notice that in App.js, we still reference it as Component1 instead of Component1WithRouter
Component2.js
import React from 'react';
class Component2 extends React.Component {
handleButtonClick() {
this.props.history.push('/render2');
}
render() {
return (
<div>
<h1>Component 2</h1>
<button onClick={this.handleButtonClick.bind(this)}>Component 2 Button</button>
</div>
);
}
}
export default Component2;
For Component2, the history object is already available from this.props. You just need to invoke the push function.
If you are using "function components" and Hooks, instead of expecting props, use the useHistory() function instead:
import {useHistory} from 'react-router-dom';
export default function MyFunctionComponent() {
const history = useHistory();
const myEventHandler = () => {
// do stuff
history.push('/newpage');
};
// ...
}
Just render a redirect component:
import { Redirect } from 'react-router';
// ...
<Redirect to="/some/path" />
See docs here: https://reacttraining.com/react-router/core/api/Redirect
I really appreciate CodinCat's answer since he helped me resolve a different error, but I found a more correct solution:
In React Router 4.0 (I don't know about previous versions) the router passes a history object to the component (i.e.: this.props.history). You can push your url onto that array to redirect:
this.props.history.push('/dogs');
In my case though, I had two levels of components, the router called a component called LoginPage, and LoginPage called a component called Login. You'll only have the history object in the props of your child object if you pass it on:
<Router>
<Route path="/dogs" component={DogsPage}/>
<Route path="/login" component={LoginPage}/>
</Router>
const LoginPage = (props) => {
// Here I have access to props.history
return (
<div>
<Login history={props.history} />
</div>
)
}
const Login = (props) => {
function handleClick(){
// Now we can simply push our url onto the history and the browser will update
props.history.push('/dogs');
}
return (
<div>
<button onClick={handleClick}>Click to Navigate</button>
</div>
)
}
I know the example above is a bit contrived, since it would be easier in this case to just use a link, but I made the example this way to keep it concise. There are many reasons you may need to navigate this way. In my case, I was doing a graphQL request and wanted to navigate to the home page once a person had successfully logged in.