react-router children not propagating - javascript

I am trying to use react-router but I am not able to propagate children components.
index.js
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';
import App from './App';
import Login from './containers/Login';
const rootElement = document.getElementById('app');
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="login" component={Login}/>
</Route>
</Router>
), rootElement);
App.js
import React, { Component, PropTypes } from 'react';
import { Login } from './containers';
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
return (
<div className="content">
{children}
</div>
);
}
}
App.propTypes = {
children: PropTypes.any,
};
LoginPage.js
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { Login } from '../components';
export default class LoginPage extends Component {
constructor(props, context) {
super(props, context);
}
handleSubmit(event) {
event.preventDefault();
}
render() {
const { handleSubmit, redirect } = this.props;
return (
<Login handleSubmit={handleSubmit}
redirect={redirect}
/>
);
}
}
LoginComponent.js
import React, { Component, PropTypes } from 'react';
export default class Login extends Component {
constructor(props, context) {
super(props, context);
this.state = {
email: '',
password: '',
};
}
handleChange(field, event) {
const nextState = this.state;
nextState[field] = event.target.value;
this.setState(nextState);
}
handleSubmit(event) {
event.preventDefault();
this.props.handleSubmit(this.state);
}
render() {
return (
<form onSubmit={event => this.handleSubmit(event)}>
<input
type="text" placeholder="Email"
value={this.state.email}
onChange={this.handleChange.bind(this, 'email')}
/>
<input
type="password" placeholder="Password"
value={this.state.password}
onChange={this.handleChange.bind(this, 'password')}
/>
<input type="submit" value="Submit"/>
</form>
);
}
}
Login.propTypes = {
handleSubmit: PropTypes.func.isRequired,
};
If I just import LoginPage directly into App.js where I try to render {children} it works perfectly fine. On inspection it simply says children is undefined
react#0.14.6
react-dom#0.14.6
react-router#2.0.0-rc5
As a side note, I ran npm list react-router and I got this back
`-- (empty)
npm ERR! code 1
Any help would be great!!
Edit: I edited the first code snippet to be import Login from './containers/Login'; from import { Login } from './containers/Login';
That was a type from simplifying the problem. I had it the other way originally because I am actually using an index.js for containers and was calling import { Login } from './containers';
I have stepped through the code and it shows that Login is NOT undefined in index.js but children is when I get to App.js
Below is a screenshot of a breakpoint in index.js and App.js in the same run. index.js shows Login as being initialized but then children is undefined.
[
Okay I have simplified the whole thing as much as possible now into a single file and it still doesn't work
import 'babel-polyfill';
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';
class App extends Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
return (
<div className="content">
{children}
</div>
);
}
}
class Child extends Component {
render() {
return (
<p>I am a child</p>
);
}
}
const rootElement = document.getElementById('app');
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="login" component={Child}/>
</Route>
</Router>
), rootElement);
I then ran it and got the following
Then I added <Child /> directly into the render property of App and got this
So this is not a problem with how I am importing files etc.

The solution is quite simple. Replace
import { Login } from './containers/Login';
with
import Login from './containers/Login';
in your index.js
The reason why your child property was always 'undefined' is because the passed over component was 'undefined':
If you have questions regarding the import syntax i can recommend this SO Question "using brackets with javascript import syntax"
See full code:
index.js
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';
import App from './App';
import Login from './containers/LoginPage';
const rootElement = document.getElementById('app');
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="login" component={Login}/>
</Route>
</Router>
), rootElement);
App.js
import React, { Component, PropTypes } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
return (
<div className="content">
{children}
</div>
);
}
}
App.propTypes = {
children: PropTypes.any,
};
./containers/LoginPage.js
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Login from '../components/Login';
export default class LoginPage extends Component {
constructor(props, context) {
super(props, context);
}
handleSubmit(event) {
event.preventDefault();
}
render() {
const { handleSubmit, redirect } = this.props;
return (
<Login handleSubmit={handleSubmit}
redirect={redirect}
/>
);
}
}
./components/Login.js
import React, { Component, PropTypes } from 'react';
export default class Login extends Component {
constructor(props, context) {
super(props, context);
this.state = {
email: '',
password: '',
};
}
handleChange(field, event) {
const nextState = this.state;
nextState[field] = event.target.value;
this.setState(nextState);
}
handleSubmit(event) {
event.preventDefault();
this.props.handleSubmit(this.state);
}
render() {
return (
<form onSubmit={event => this.handleSubmit(event)}>
<input
type="text" placeholder="Email"
value={this.state.email}
onChange={this.handleChange.bind(this, 'email')}
/>
<input
type="password" placeholder="Password"
value={this.state.password}
onChange={this.handleChange.bind(this, 'password')}
/>
<input type="submit" value="Submit"/>
</form>
);
}
}
Login.propTypes = {
handleSubmit: PropTypes.func.isRequired,
};
Proof with react 0.14.6 and react-router 2.0.0-rc5

Ok so answering my own question. Basically a really stupid mistake but maybe someone will benefit. I was using localhost/#/child because I thought this is what it was supposed to say and localhost/child hits an registered route on my server. So the fix was to make my server-side route handler
router.get('/*', (req, res) => {
res.render(view);
});
And then navigate to localhost/child

Related

React Redux routing issues

I have recently experienced some issues with my react router and redux. Basically, I have a redux value set which let's me know if an item is selected. If the item is selected, then it will allow a URL to be used. One thing that I have noticed. If I add a redirect function. It breaks everything
Authentication function:
import React, { Component, Fragment } from "react";
import { Provider } from "react-redux";
// import store from "./store";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom";
import { connect } from "react-redux";
import Profile from "./Profile";
import AddDomain from "./AddDomain";
import ChoosePackage from "./ChoosePackage";
import DashboardHome from "./DashboardHome";
import { Elements } from "#stripe/react-stripe-js";
import PropTypes from "prop-types";
import { loadStripe } from "#stripe/stripe-js";
const stripePromise = loadStripe("pk_test_7S0QSNizCdsJdm9yYEoRKSul00z4Pl6qK6");
class index extends Component {
componentDidMount() {
console.log("DOMAIN NAME" + this.props.domain_name);
}
state = {
domain_name: ""
};
static propTypes = {
domain_name: PropTypes.string.isRequired
};
domainCheck = () => {
if (this.props.domain_name != "") {
return <ChoosePackage />;
} else {
console.log("running rediect");
return <Redirect to="/dashboard" />;
}
};
render() {
return (
<React.Fragment>
<Route path="/dashboard/add-domain/choose-package">
{this.domainCheck()}
</Route>
<Route exact path="/dashboard/add-domain">
<AddDomain />
</Route>
<Route exact path="/dashboard/profile">
<Profile />
</Route>
<Route exact path="/dashboard">
<DashboardHome />
</Route>
</React.Fragment>
);
}
}
const mapStateToProps = state => ({
domain_name: state.domain.domain_name
});
index.defaultProps = {
domain_name: ""
};
export default connect(mapStateToProps, { pure: false })(index);
Any help is greatly appreciated

Loader icon Constantly Displaying In my App.JS

I am trying to add a loader icon to my site but it is constantly showing should i put it in a component and import it? The state is set to true as the page is loading when it refreshes maybe its something with this.
This is my app.js file
import React from "react";
import { createBrowserHistory } from "history";
import { Router, Route, Switch } from "react-router-dom";
import "assets/scss/material-kit-react.scss?v=1.4.0";
// pages for this product
import LandingPage from "views/LandingPage/LandingPage.jsx";
import ProfilePage from "views/ProfilePage/ProfilePage.jsx";
import { css } from "#emotion/core";
// First way to import
import { ClipLoader } from "react-spinners";
const override = css`
display: block;
margin: 0 auto;
border-color: red;
`;
var hist = createBrowserHistory();
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true
};
}
render() {
return (
<>
<ClipLoader
css={override}
sizeUnit={"px"}
size={150}
color={"#123abc"}
loading={this.state.loading}
/>
<Router history={hist}>
<Switch>
<Route path="/profile-page" component={ProfilePage} />
<Route path="/" component={LandingPage} />
</Switch>
</Router>
</>
);
}
}
your default state is set to true. so it will show the loading once the component renders
this.state = {
loading: false
};
You can change your Loader to a HOC
import React from 'react';
import { ClipLoader } from "react-spinners";
function Loading(Component) {
return function WihLoadingComponent({ isLoading, ...props }) {
if (!isLoading) return (<Component {...props} />);
return (<ClipLoader
css={override}
sizeUnit={"px"}
size={150}
color={"#123abc"}
loading={isLoading}
/>);
}
}
export default Loading;
Then you can use you global context or redux store to update the state of isLoading
render() {
return (
<Loading isLoading={this.props.isLoading} data={data} />
)
}

need to pass EmaiID and password from login component to home component react

React version 15.2.1
Can someone please tell me how to pass around data between components. I need to pass email and password from Login.js to Products.js where I'm making a http request to the server in the function display().
Login.js
import React from "react";
import { Button, FormGroup, FormControl, ControlLabel } from "react-bootstrap";
export class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
}
validateForm() {
return this.state.email.length > 0 && this.state.password.length > 0;
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleSubmit = async event => {
event.preventDefault();
try {
this.props.history.push("/products");
} catch (e) {
alert(e.message);
}
}
render() {
return (
<div className="Login">
<form onSubmit={this.handleSubmit}>
<FormGroup controlId="email" bsSize="large">
<ControlLabel>Email</ControlLabel>
<FormControl
autoFocus
type="email"
value={this.state.email}
onChange={this.handleChange}
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
<ControlLabel>Password</ControlLabel>
<FormControl
value={this.state.password}
onChange={this.handleChange}
type="password"
/>
</FormGroup>
<Button
block
bsSize="large"
disabled={!this.validateForm()}
type="submit"
>
Login
</Button>
</form>
</div>
);
}
}
Products.js
import React from 'react';
export class Products extends React.Component {
display(){
var http = new XMLHttpRequest();
var url = ""
//have hardcoded username and password here for now. need to get it from Login component
http.send()
return http.responseText
}
render() {
var x=this.display();
var jsonObject = JSON.parse(x)
// console.log(jsonObject)
var conArray=[]
for (var y=0; y < jsonObject.length; y++){
console.log(jsonObject[y]['completeName'])
conArray.push(jsonObject[y]['completeName'])
}
return (
<div>
<h2>Product Names</h2>
{
conArray.map(t => <div>{t}</div>)
}
</div>
);
}
}
index.js
import React from 'react';
import { render } from 'react-dom';
import {BrowserRouter as Router} from "react-router-dom";
import Route from 'react-router-dom/Route';
import { browserHistroy, Switch } from 'react-router';
import createBrowserHistory from 'history/createBrowserHistory';
const newHistory = createBrowserHistory();
import { Products } from "./components/Products";
import { Login } from "./components/Login";
import "babel-polyfill";
class App extends React.Component {
render() {
return (
<Router history={newHistory}>
<Switch>
<Route path="/" exact component={Login}/>
<Route path="/products" exact component={Products}/>
</Switch>
</Router>
);
}
}
render(<App />, window.document.getElementById('app'));
One way of doing it is Passing props through state variable of history
some-thing like this:
this.props.history.push({
pathname: '/products',
state: { email: this.state.email,
password: this.state.password }
})
and you can access these in your products.js using this.props.location.state.email and this.props.location.state.password and one more thing is don't forget to wrap your product.js component with withRouter to be able to connect and use the values in /products

react router - unable to pass store down

I am unable to make the store available to children components.
The setup is a SPA with Symfony as back-end, though this should not make a difference for this matter.
The entry point for Webpack is the file:
/client/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import ReduxPromise from 'redux-promise';
import Root from './App';
import registerServiceWorker from './registerServiceWorker';
import reducers from './pages/combine_reducers';
let composeEnhancers = typeof(window) !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
reducers,
composeEnhancers(
applyMiddleware(ReduxPromise)
)
)
ReactDOM.render(
<Root store={store} />
, document.querySelector('#root')
);
registerServiceWorker();
The apps as such is at:
/client/App.js
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
So far so good. The store is available in App.js.
But that's not the case at the next level. As you can see I'm attempting to make the store available using connect().
/client/pages/home/index.js
import React from 'react';
import { connect } from 'react-redux';
import Register from '../common/register/';
import PropTypes from 'prop-types';
class Home extends React.Component {
constructor(props){
super(props)
console.log(props);
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
Home.propTypes = {
store: PropTypes.object.isRequired
}
const mapStateToProps = (state) => {
return {
store: state.store,
}
}
export default connect(mapStateToProps)(Home)
At the lower level, the Register component, I'm able to submit the form, but the store not being available, I am unable to capture the response coming from the server.
/client/pages/common/register/index.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
import PropTypes from 'prop-types';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve, reject) => {
actionSubmitRegister(this.props.form.RegisterForm.values);
});
submitForm.then((response) => {
console.log('response',response);
this.setState({registerResponse: this.props.submit_register.data});
console.log('registerResponse', this.state.registerResponse);
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
/*
Register.propTypes = {
store: PropTypes.object.isRequired
}
*/
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
In mapStateToProps you map store: state.store but in general you use this method to map single props from your state to props in your component, not map the entire store (if this is even possible).
Eg:
form: state.form
The reason you are not able to access the store object in props is because you are not passing it down via props.
Provider from the react-redux library, makes it available to all children down the element tree. Store is made available via React's context API, NOT via props.
"Context is designed to share data that can be considered “global” for a tree of React components."
So in a child component of Provider, we can now do something like
render() {
const { store } = this.context;
console.log(store)
return(
...
)
}
This is the same way that react-redux's connect HOC is able to access the store and subsequently mapStateToProps or utilise the store's dispatch method to mapDispatchToProps.
Also I think Provider requires that it’s child element is a React component.
Check out this tutorial for a more in-depth explanation.
After the input I received above, I reviewed my code and got it to work.
Actually the main issue was on the /client/pages/common/register/index.js file, but I am posting the whole chain for reference:
/client/index.js
nothing to change
/client/App.js
The references to propTypes do not seem to be necessary, so I took them out.
import React from 'react';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
export default Root;
/client/pages/home/index.js
Here both propTypes and connect() do not seem to be required.
import React from 'react';
import Register from '../common/register/';
class Home extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
export default Home;
/client/pages/common/register/index.js
The main issue here was the onSubmitRegister() method. The promise was not properly setup and I was referencing the action directly instead of using this.props. React do not seem to like that.
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve) => {
resolve(this.props.actionSubmitRegister(this.props.form.RegisterForm.values));
});
submitForm.then((result) => {
let data = result.payload.data;
this.setState({registerResponse: data.message});
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);

react js conditional rendering fails

I'm trying to render based on whether or not a user has logged in.
Below is my LoginForm.js as well as my Index.js and Dashbaord.js. I'm trying to get from LoginForm.js to Dashboard.js but it doesn't work. No errors, but the content of the Dashboard.js file shows for a brief moment before reverting back to the LoginForm.js. What am I doing wrong here?
LoginForm.js
// jshint esversion: 6
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Dashboard from './Dashboard.js';
class LoginForm extends Component {
constructor(props) {
super(props);
this.props = props;
this.state = {
pic: '//ssl.gstatic.com/accounts/ui/avatar_2x.png',
isLoggedIn: false
};
}
_login(){
let username = document.getElementById('inputEmail').value;
let password = document.getElementById('inputPassword').value;
this.options = {
username: username,
password: password
};
this.props.client.login(this.options, (success, data)=>{
if (success) {
console.log('You are now logged in', data);
this.setState({isLoggedIn: true});
}else{
console.log('Details incorrect or something went wrong.');
}
});
}
handleSubmit(){
this._login();
}
render() {
let login = this.state.isLoggedIn;
return (
<div className='container'>
{login ? (
<Dashboard/>
) : (
<div className='card card-container'>
<img alt='profile-img' id='profile-img' className='profile-img-card' src={this.state.pic} />
<p id='profile-name' className='profile-name-card'></p>
<form className='form-signin' onSubmit={this.handleSubmit.bind(this)}>
<span id='reauth-email' className='reauth-email'></span>
<input type='text' id='inputEmail' className='form-control' placeholder='Username'></input>
<input type='password' id='inputPassword' className='form-control' placeholder='Password'></input>
<button className='btn btn-lg btn-success btn-block btn-signin' type='submit'>Login</button>
</form>
</div>
)}
</div>
);
}
}
export default LoginForm;
Dashboard.js
// jshint esversion: 6
import React, { Component } from 'react';
class Dashboard extends Component {
render() {
return (
<article>
<section className='text-section'>
<h1>Dashboard</h1>
<p>
Welcome, you are logged in! To have a look at the code behind this application, go to Github.
</p>
</section>
</article>
)
}
}
export default Dashboard;
And here's my Index.js with the Router in.
// jshint esversion: 6
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Dashboard from './components/Dashboard.js';
import { Router, Route } from 'react-router';
import registerServiceWorker from './registerServiceWorker';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
ReactDOM.render(
<Router history={history}>
<Route exact path="/" component={App}>
<Route exact path="/dashboard" component={Dashboard}/>
</Route>
</Router>,
document.getElementById('root'));
registerServiceWorker();

Categories