I am in need of some help. I'm working on a create-react-app using React-Router. The issue that I'm seeing is that when I run my app, I should be able to see individual student information when I click on their name. Instead, nothing renders.
Here is the code for the component (students.js) that is not rendering:
import React, { Component } from 'react';
import axios from 'axios';
export default class Student extends Component {
constructor() {
super();
this.state = {
studentInfo: {}
}
}
componentDidMount(){
console.log('fired')
axios.get(`http://localhost:3005/students/${this.props.match.params.id}`)
.then(res => {
this.setState({studentInfo: res.data})
})
.catch(err => console.log(err))
}
render() {
return (
<div className="box">
<h1>Student:</h1>
<h1>{this.state.studentInfo.first_name}
{this.state.studentInfo.last_name}</h1>
<h3>Grade: {this.state.studentInfo.grade}</h3>
<h3>Email: {this.state.studentInfo.email}</h3>
</div>
)
}
}
I have tried a console.log in the componentDidMount method of my students.js file to see if it is mounting properly and I found that it would not fire. When I tried to check state with the React Dev Tools, I saw that there was no state.
I have a similar component called classList.js that has a similar setup that is working properly.
classList.js:
import React, { Component } from 'react';
import axios from 'axios';
import {Link} from 'react-router-dom';
export default class ClassList extends Component {
constructor() {
super();
this.state = {
students: []
}
}
componentDidMount(){
axios.get(`http://localhost:3005/students?
class=${this.props.match.params.class}`)
.then(res => {
this.setState({students: res.data});
})
.catch(err => console.log(err))}
render() {
const students = this.state.students.map((student,i) =>
<Link key={i} to={`/student/${student.id}`}><h3>{student.first_name}
{student.last_name}</h3></Link>
)
return (
<div className="box">
<h1>{this.props.match.params.class}</h1>
<h2>ClassList:</h2>
{students}
</div>
)
}
}
I've checked my routes and my link tags and they seem fine.
Routes.js:
import React from 'react';
import {Switch, Route} from 'react-router-dom';
import Home from './components/Home/Home';
import About from './components/About/About';
import ClassList from './components/ClassList/ClassList';
import Student from './components/Student/Student'
export default (
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/classlist/:class' component={ClassList} />
<Route path='student/:id' component={Student} />
</Switch>
)
Any thoughts? Thanks!
I'm pretty sure it doesn't find the student route because you're missing an /
<Route path='student/:id' component={Student} />
should be
<Route path='/student/:id' component={Student} />
Related
I'm testing some function, I discover a solution but I'm not sure that is correct way.
I need to load data from API so I used axios, and I need to share data between some child routes, I am currently using simple setState to keep values and passed them to the Route Components to be used.
Here is the MainContainer component
import React from 'react';
import axios from 'axios';
import { BrowserRouter, Route, Switch, Link, HashRouter, useLocation } from 'react-router-dom';
import Languages from './pages/Languages';
import Home from './pages/Home';
export default class MainContainer extends React.Component
{
constructor(props) {
super(props);
this.state = {
languages: null,
language:null,
}
}
get_init(language)
{
console.log("get_init")
var params = {
language:language,
}
axios
.get("https://xxxx.com/wp/it/api/init/")
.then(function(result) {
this.setState({languages:result.data.languages});
}.bind(this));
}
render() {
console.log("render")
return (
<div>
<Switch>
<Route path="/" component={() => <Languages get_init={this.get_init.bind(this)} languages={this.state.languages} />} exact />
<Route path="/:language/" component={(urlparam) => <Home language={urlparam.match.params.language} get_init={this.get_init.bind(this)} languages={this.state.languages} />} exact/>
<Route component={Error}/>
</Switch>
</div>
);
}
}
My Language Component is bellow
import axios from 'axios';
import React from 'react';
import LoaderData from './../helpers/LoaderData';
import { Link } from 'react-router-dom';
export default class Languages extends React.Component
{
constructor(props)
{
super(props);
if (props.languages==null)
{
this.props.get_init();
}
}
render() {
if (this.props.languages==null)
{
return '';
}
return (
<div>
<div className='main'>
LANGUAGE {Math.random()}<br/>
{
this.props.languages.map((language, i) =>
{
return (<div key={language.prefix}><Link to={language.prefix+"/"} >{language.name}</Link> <br/></div>)
})
}
</div>
</div>
);
}
}
is a correct way to build a website in react or there are better ways?
I'm new to Redux, using it with React, and am in need of some help. I have a menu that when a menu item is clicked, another component needs to update some copy. I'm able to dispatch an action and have the store update. However, I can't get the child component (HeroText) to render the new store value in the store.subscribe method when the store values change. Please help and thanks!
import React, { Component } from "react";
import ReactDOM from "react-dom";
import HeroText from "../presentational/HeroText.jsx";
import bgImage from "../../../images/forest_fog.jpg";
import AnantaNavbar from "../presentational/AnantaNavbar.jsx";
import '../../../scss/hero.scss';
import store from '../../store/index';
import { connect } from "react-redux";
const mapStateToProps = state => {
return {
contact: state.contact,
heroText: state.heroText
}
}
class HeroContainer extends Component {
constructor(props)
{
super(props);
this.state = store.getState();
store.subscribe(() => {
console.log(store.getState().heroText);
this.setState({
heroText: store.getState().heroText,
})
})
}
render()
{
return (
<div id="hero-container" style={{backgroundImage: ("url(" + bgImage + ")") || ""}}>
<div className="container">
<HeroText text={this.props.heroText}>
Welcome back {this.props.contact.full_name}
</HeroText>
<AnantaNavbar></AnantaNavbar>
</div>
</div>
);
}
}
export default connect(mapStateToProps)(HeroContainer);
UPDATE
Below is my parent App Container with Provider
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Navbar, NavbarBrand, NavbarNav, NavbarToggler, Collapse, NavItem, NavLink, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'react-bootstrap';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import LoginContainer from '../../../js/components/container/LoginContainer.jsx';
import DashboardContainer from '../../../js/components/container/DashboardContainer.jsx';
import HomeContainer from '../../../js/components/container/DashboardContainer.jsx';
import ProfileContainer from '../../../js/components/container/ProfileContainer.jsx';
import HeroContainer from "./HeroContainer.jsx";
import '../../../scss/globals.scss';
import logo from '../../../images/logo1.png';
import { Provider } from 'react-redux';
import store from '../../store/index';
const Router = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={LoginContainer} />
<Route exact path="/login" component={LoginContainer} />
<Route exact path="/home" component={HomeContainer} />
<React.Fragment>
<HeroContainer />
<Route path="/dashboard" component={DashboardContainer} />
<Route path="/profile" component={ProfileContainer} />
</React.Fragment>
</Switch>
</BrowserRouter>
);
class AppContainer extends Component {
constructor(props)
{
super(props);
this.state = {
};
}
componentDidMount()
{
}
render()
{
return (
<div>
<Provider store={store}>
<Router></Router>
</Provider>
</div>
);
}
}
export default AppContainer;
The default heroText in the store says "DASHBOARD". When a menu item is clicked, in this case a link to /profile, the heroText should update to "PROFILE" after updating the store.
You can see in the console that the store is changing, but the "DASHBOARD" copy is not reflecting.
RESOLVED
I got this working with the code below. Thanks for all the help!
import React, { Component } from "react";
import ReactDOM from "react-dom";
import HeroText from "../presentational/HeroText.jsx";
import bgImage from "../../../images/forest_fog.jpg";
import AnantaNavbar from "../presentational/AnantaNavbar.jsx";
import '../../../scss/hero.scss';
import store from '../../store/index';
import { connect } from "react-redux";
const mapStateToProps = state => {
return {
contact: state.contact,
heroText: state.heroText
}
}
class HeroContainer extends Component {
constructor(props)
{
super(props);
}
render()
{
return (
<div id="hero-container" style={{backgroundImage: ("url(" + bgImage + ")") || ""}}>
<div className="container">
<HeroText text={store.getState().heroText}>
Welcome back {store.getState().contact.full_name}
</HeroText>
<AnantaNavbar></AnantaNavbar>
</div>
</div>
);
}
}
export default connect(mapStateToProps)(HeroContainer);
Since you are trying to get state from Redux, there's no pointing in keeping it in local state. Plus, you don't need to use store.getState, connect already does that for you.
const mapStateToProps = state => {
return {
contact: state.contact,
heroText: state.heroText
}
}
class HeroContainer extends Component {
render() {
return (
<div id="hero-container" style={{backgroundImage: ("url(" + bgImage + ")") || ""}}>
<div className="container">
<HeroText text={this.props.heroText}>
Welcome back {this.props.contact.full_name}
</HeroText>
<AnantaNavbar></AnantaNavbar>
</div>
</div>
);
}
}
export default connect(mapStateToProps)(HeroContainer);
You also need to make sure that your app is wrapped in a provider, like this:
<Provider store={store}>
<App />
</Provider>
I'm creating a router in App.js file.
I'm creating a login page and I change page with this.props.history.push('/homepage') to go to the homepage. It works.
Now I'm in /homepage and i would like to do the same but it don't works ...
I make: this.context.history.push('/test'); and I've got an error "TypeError: Cannot read property 'push' of undefined".
I already tried to use "this.context.router.history.push('/page');" but i have the same error. I tried to use browserHistory instead of HashRouter but it don't works.
The only thing who work is to create functions and to call them with the buttonclick. But i can't do it with the react way !
Include App.js:
import React, {Component} from 'react';
import {HashRouter as Router, Route} from 'react-router-dom';
import HomePage from './dashboard/Dashboard';
import Register from "./login/Register"
import Login from "./login/Login"
import Admin from "./admin/Admin"
class App extends Component {
render() {
return (
<Router basename="/">
<div>
<Route exact path="/" component={Login}>
</Route>
<Route exact path="/homepage" component={HomePage}>
</Route>
<Route exact path="/register" component={Register}>
</Route>
<Route exact path="/login" component={Login}>
</Route>
<Route exact path="/admin" component={Admin}>
</Route>
</div>
</Router>
);
}
}
export default App;
Include Login file
import React, {Component} from 'react';
import {Link, NavLink} from 'react-router-dom';
import O2Connexion from './O2Connexion'
import "../App.css"
import Axios from 'axios'
import AreaLogo from '../ressources/arealogo.png'
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
test: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
let target = e.target;
let value = target.type === 'checkbox' ? target.checked : target.value;
let name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(e) {
e.preventDefault();
this.props.history.push('/homepage')
}
}
render() {
...
Include NavBar file
import React, { Component } from 'react';
import { Layout, Header, Navigation, Content } from 'react-mdl';
import {HashRouter as Router, Route, Link} from 'react-router-dom';
class Navbar extends Component {
constructor(props) {
super(props);
this.test = this.test.bind(this);
}
test(e) {
e.preventDefault();
//ne marche pas comme ça
this.context.history.push('/homepage');
}
render() {
return (
<div style={{height: '300px', position: 'relative'}}>
<Layout fixedHeader>
<Header title={<span><span style={{ color: '#ddd' }}>Area</span></span>}>
<Navigation>
<a onClick={this.test}>Home Page</a>
<a>Admin</a>
<a>Log out</a>
</Navigation>
</Header>
<Content />
</Layout>
</div>
);
}
}
export default Navbar;
So it don't works on the navabar file and i have no idea why
Can someone explain me why it don't works and if it exist a "reactjs" way to do it ? Thank you and have a nice evenning
You have to wrap your components with withRouter hoc like:
import React, { Component } from 'react';
import { withRouter } from 'react-router';
class Navbar extends Component { ... }
const NavbarWithRouter = withRouter(Navbar);
export default NavbarWithRouter;
Doing this way you could access match, location, history inside Navbar's component props
You need to repeat steps above for each component where you need access for seeking properties
My links in the following code are not working. I'm a beginner and I'm not sure on where the issue is.
Do you have any tips on how to debug that?
Thank you,
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import {fetchItems} from './actions/items';
import { connect } from 'react-redux';
import './App.css';
import Home from './components/Home.js'
import Additem from './components/Additem'
import Mybag from './components/Mybag.js'
import About from './components/About.js'
import ItemShow from './components/ItemShow.js'
import NavigationBar from './components/NavigationBar.js'
class App extends Component {
constructor(props){
super(props)
}
componentDidMount(){
this.props.fetchItems();
}
render() {
console.log("itemList: ", itemList)
const itemList = this.props.items
return (
<div className="App">
<NavigationBar />
<React.Fragment>
<Route exact path='/' render={routerProps => <Home {...routerProps} items={itemList}/>} />
<Route exact path={`/items/:itemID`} component={ItemShow} />
<Route exact path="/my_bag" component={Mybag} />
<Route exact path="/add_item" component={Additem} />
<Route exact path="/about" component={About} />
</React.Fragment>
</div>
)
}
}
const mapStateToProps = state => {
return {
items: state.items
}
}
const mapDispatchToProps = dispatch => {
return {
fetchItems: (items) => dispatch(fetchItems(items)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
I used to have a component in charge of fetching my items in the DB and load them. It was working but refactored to include the fetch as a redux action and since then, it is not working anymore.
Please let me know if you have any tips.
Thank you!
Wrap your connected component with react-router's withRouter function:
// Adding the imports just for clarity.
import { connect } from "react-redux";
import { compose } from "redux";
import { withRouter } from "react-router-dom";
// compose-style
compose(
withRouter,
connect(...)
)(YourComponent);
// Without compose
withRouter(connect(...))(Your component);
For more information: withRouter API
sn42 is right. But I prefer to export the container with withRouter function rather than using compose
export default withRouter((connect(mapStateToProps, mapDispatchToProps)(App)));
I have a categories index page which links to a products index page of products specific to that category. That much is functioning. But when I attempt to click on a product linked to a show component specific to that product I'm encountering trouble. Below is my code:
router.js
import React from 'react';
import { Router, Route, Switch } from 'react-router';
import createBrowserHistory from 'history/createBrowserHistory'
import App from './App';
import CategoriesIndexPage from './pages/categories/CategoriesIndexPage';
import ProductsIndexPage from './pages/products/ProductsIndexPage';
import ProductShow from './pages/products/ProductShow';
import LocationsPage from './pages/LocationsPage';
const history = createBrowserHistory()
const router = (
<Router history={history}>
<Switch>
<Route exact path='/' component={App}/>
<Route path='/categories' component={CategoriesIndexPage}/>
<Route path='/locations' component={LocationsPage}/>
<Route path='/:category' component={ProductsIndexPage}>
<Route path='/:id' component={ProductShow}/>
</Route>
</Switch>
</Router>
);
export default router;
ProductIndexPage.js
import React, { Component } from 'react';
import { BWReactData } from '../../config/FirebaseConstants.js';
import Head from '../../components/Head.js';
import Foot from '../../components/Foot.js';
import ProductsIteration from './ProductsIteration';
class ProductsIndexPage extends Component {
constructor(props){
super(props);
this.state = {
allProducts: [],
loading: true,
}
}
componentDidMount() {
...
}
render() {
let allProducts = this.state.allProducts;
let loading = this.state.loading;
let categoryURL = this.props.location.state.category;
return (
<div>
<Head/>
<ProductsIteration
allProducts={allProducts}
loading={loading}
categoryURL={categoryURL}
/>
<Foot/>
</div>
)
}
}
export default ProductsIndexPage;
ProductsIteration.js
import React from 'react';
import { Link } from 'react-router-dom';
import { Col, Row } from 'react-materialize';
const ProductsIteration = props => {
let category = props.categoryURL;
if (props.loading) {
return <div>Loading...</div>
}
return (
<Row>
{props.allProducts.map(function(object) {
return (
<Col s={12} m={6} l={3} key ={object.id}>
<div style={styles.wrapper}>
<Link to={{ pathname: `${category}/${object.id}`, state: { id: object.id }}}>
<img src={object.img} style={styles.image} />
<div style={styles.description}>
<div style={styles.descriptionContent}>{object.name}</div>
</div>
</Link>
</div>
</Col>
)
})}
</Row>
)
}
export default ProductsIteration;
The link within my iteration component renders the '/:category/:id' url in my navbar but the page does nothing. This is my first project using router and any guidance would be much appreciated.
In React Router v4:
Router components are imported from 'react-router-dom' rather than 'react-router'.
The traditional <Router/> component has been replaced with the <BrowserRouter/> component, which requires no props.
Nesting routes is no longer convention. Instead, you'll have to nest your <ProductShow/> as a component prop of a <Route/> component within a <Switch/> component within your <ProductIndexPage/> component.
See below for an example.
Router.js:
// React.
import React from 'react'
// React Router DOM.
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom'
// Routes.
import App from './App'
import CategoriesIndexPage from './pages/categories/CategoriesIndexPage'
import ProductsIndexPage from './pages/products/ProductsIndexPage'
import LocationsPage from './pages/LocationsPage'
// Router.
const Router = (
<Router>
<Switch>
<Route exact path='/' component={App}/>
<Route path='/categories' component={CategoriesIndexPage}/>
<Route path='/locations' component={LocationsPage}/>
<Route path='/:category/:id?' component={ProductsIndexPage}/>
</Switch>
</Router>
)
// Export.
export default Router
ProductIndexPage.js:
// React.
import React from 'react'
// BW React Data.
import {
BWReactData
} from '../../config/FirebaseConstants.js'
// Head.
import Head from '../../components/Head.js'
// Foot.
import Foot from '../../components/Foot.js'
// Products Iteration.
import ProductsIteration from './ProductsIteration'
// Product Show.
import ProductShow from './ProductShow'
// React Router DOM.
import {
Switch
} from 'react-router-dom'
// Products Index Page.
class ProductsIndexPage extends React.Component {
// Constructor.
constructor(props){
// Super Props.
super(props)
// State.
this.state = {
allProducts: [],
loading: true,
}
}
// Did Mount.
componentDidMount() {
...
}
// Render.
render() {
let allProducts = this.state.allProducts
let loading = this.state.loading
let categoryURL = this.props.location.state.category
return (
<div>
<Head/>
<ProductsIteration
allProducts={allProducts}
loading={loading}
categoryURL={categoryURL}
/>
{this.props.match.params.id ? (<ProductShow/>) : ''}
<Foot/>
</div>
)
}
}