Redux Component not receiving children - reactjs

So I am developing a React + Redux app.
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={AppContainer}>
<IndexRoute component={Home}/>
<Route path="login" component={LoginContainer}/>
<Route path="protected" component={Protected} onEnter={checkUserIsLoggedIn}/>
<Route path="*" component={NotFound}/>
</Route>
</Router>
</Provider>
AppContainer.jsx
const App = function(props){
var isAuthenticated = props.login.isAuthenticated;
return <Grid>
<h1>Welcome to app!</h1>
<nav>
{isAuthenticated ? <Link to="/logout">Logout</Link> : <Link to="/login">Login</Link> }|
<Link to="/">Home</Link> |
<Link to="/protected">Protected</Link> |
</nav>
<div>
{props.children}
</div>
</Grid>
};
const mapStateToProps = (state) => {
var loginState = state.login;
return {
login: {
apiToken: loginState.apiToken,
isAuthenticated: loginState.isAuthenticated
}
}
};
const AppContainer = connect(
mapStateToProps
)(App);
Home, NotFound and Protected are stateless components, i.e.
const Home = (props) => <h2>Home</h2>;
While Login.jsx
import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import { Row, Col, Panel, Button, Alert, Form, FormGroup, FormControl, InputGroup, Glyphicon } from 'react-bootstrap'
class Login extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.activateSignup = this.activateSignup.bind(this);
}
onSubmit (e) {
e.preventDefault();
let username = ReactDOM.findDOMNode(this.refs.username).value;
let password = ReactDOM.findDOMNode(this.refs.password).value;
this.props.onLoginSubmit(username, password);
}
activateSignup (e){
alert('does nothing for now');
}
render () {
return (
<Col style={{marginTop: "50px"}} md={6} mdOffset={3} sm={8} smOffset={2}>
<Panel header="Sign In" bsStyle="info">
<Col sm={12}>
{this.props.apiToken && <Alert bsStyle="success">
<strong>Welcome!</strong>
</Alert>}
</Col>
<Form horizontal onSubmit={this.onSubmit}>
<InputGroup style={{marginBottom: "25px", paddingTop: "15px"}}>
<InputGroup.Addon><Glyphicon glyph="user"/></InputGroup.Addon>
<FormControl ref="username"
type="text"
placeholder="username or email"
required/>
</InputGroup>
<InputGroup style={{marginBottom: "25px"}}>
<InputGroup.Addon><Glyphicon glyph="lock"/></InputGroup.Addon>
<FormControl ref="password"
type="password"
placeholder="password"
required/>
</InputGroup>
<InputGroup>
<FormGroup>
<Col sm={12}>
<Button id="btn-login" bsStyle="success" type="submit" disabled={this.props.isFetching}>Login</Button>
</Col>
</FormGroup>
<FormGroup>
<Col md={12}>
<div style={{borderTop: "1px solid#888", paddingTop: "15px", "fontSize": "85%"}}>
Don't have an account!{' '}
<a href="#" onClick={this.activateSignup}>
Sign Up Here
</a>
</div>
</Col>
</FormGroup>
</InputGroup>
</Form>
</Panel>
</Col>
)
}
}
Login.propTypes = {
isFetching: PropTypes.bool,
error: PropTypes.string,
apiToken: PropTypes.string
};
export default Login;
LoginContainer.jsx
import { connect } from 'react-redux'
import { fetchLogin } from '../actions/login-actions'
import Login from '../views/Login.jsx'
const mapStateToProps = (state) => {
return state.login
};
const mapDispatchToProps = (dispatch) => {
return {
onLoginSubmit: (username, password) => {
dispatch(fetchLogin(username, password))
}
}
};
const LoginContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Login);
export default LoginContainer
So, I don't know why this is happening but I think it is because of the misuse between Redux and ReactRouter.
My /login renders perfectly. However, whenever I go to another route, the children components are not rendered because App.props.children is always null when using AppContainer. However, if I change the route path "/" component to use App instead of AppContainer, children are rendered successfully.
What am I doing wrong?

Related

React-Bootsrap Multiple Modals

I have a navbar with the usual login and sign-up buttons. When I click the relevant link, a modal with the form should pop-up allowing the user to login or sign-up. At the moment, neither of the pop-ups seem to work. I have been following React-Bootstrap Multiple Modal and have been using the chosen answer to try and implement the modals.
The handle functions are in my app.jsx:
import React, {useEffect, useState} from 'react'
import {useRouter} from 'next/router'
import {SessionProvider, useSession} from 'next-auth/react'
import {SSRProvider} from 'react-bootstrap'
import 'application.css';
import Navigation from "../components/Navigation";
import Login from "../components/Login";
import SideNav from "../components/SideNav";
import SignUp from "../components/SignUp";
function MyApp({Component, pageProps}) {
let id = '';
const [show, setShow] = useState(null)
function handleClose() {
setShow({show: id});
}
function handleShow(id) {
setShow({show: id});
}
const [showSideBar, setShowSideBar] = useState(false)
const toggleSideMenu = () => setShowSideBar((prev) => !prev);
return (
<SessionProvider session={pageProps.session}>
<SSRProvider>
<SideNav showSideBar={showSideBar} />
<Navigation
handleShow={handleShow}
handleClose={handleClose}
toggleSideMenu={toggleSideMenu}
/>
<Login
show={show}
handleShow={handleShow}
handleClose={handleClose}
/>
<SignUp
show={show}
handleShow={handleShow}
handleClose={handleClose}
/>
{Component.auth
? <Auth><Component {...pageProps} /></Auth>
: <Component {...pageProps} />
}
</SSRProvider>
</SessionProvider>
)
}
function Auth({children}) {
// #ts-ignore
const [session, loading] = useSession()
const isUser = !!session?.user
const router = useRouter()
useEffect(() => {
if (loading) return // Do nothing while loading
if (!isUser) router.push('/login')
// If not authenticated, force log in
}, [isUser, loading])
if (isUser) {
return children
}
// Session is being fetched, or no user.
// If no user, useEffect() will redirect.
return <div>Loading...</div>
}
export default MyApp
I pass the show, handleshow, handleclose to each component. In the navigation the buttons look like this:
<Nav.Link href="#" onClick={() => handleShow('login')}><FontAwesomeIcon icon={solid('sign-in-alt')}/><span> Login</span></Nav.Link>
<Nav.Link href="#" onClick={() => handleShow('signup')}><FontAwesomeIcon icon={solid('user-plus')}/><span> Sign up</span></Nav.Link>
And then finally, my Login.jsx with a modal:
import {getCsrfToken, signIn, useSession} from "next-auth/react";
import Router from "next/router";
import { Modal, CloseButton, Form } from 'react-bootstrap';
import Link from "next/link";
import { useState } from "react";
function Login({csrfToken, show, handleClose, handleShow, props}) {
const [error, setError] = useState(false);
//setShow(prev => !prev);
const handleSubmit = async (e) => {
e.preventDefault();
const res = await signIn('credentials', {
redirect: false,
email: e.target.email1.value,
password: e.target.password1.value,
callbackUrl: `/dashboard`,
});
if (res?.error) {
setError(true);
} else {
Router.push('/dashboard');
}
}
return (
<Modal show={show === "login"} onHide={handleClose} fade={false}>
<Modal.Header className={"modal-dark bg-dark"}>
<Modal.Title><h1>Log In</h1></Modal.Title>
<CloseButton variant="white" onClick={handleClose}/>
</Modal.Header>
<Modal.Body className={"modal-dark bg-dark"}>
<form noValidate onSubmit={(e) => handleSubmit(e)}>
<input name="csrfToken" type="hidden" defaultValue={csrfToken}/>
<Form.Group className="mt-3">
<Form.Control
id="email-address1"
name="email1"
type="email"
autoComplete="email"
required
className=""
placeholder="Email address"
/>
</Form.Group>
<Form.Group className="mt-3">
<Form.Control
id="password1"
name="password1"
type="password"
autoComplete="current-password"
required
placeholder="Password"
/>
</Form.Group>
<button type={"submit"} className={"btn orange-button mt-3"}>Login</button>
<div>
or <Link href='/signup'>sign up</Link>
</div>
{error && <div className="bg-red-300 p-2 text-white rounded">Wrong email or password</div>}
</form>
</Modal.Body>
<Modal.Footer className={"modal-dark bg-dark"}>
<button onClick={handleClose}>
Close
</button>
</Modal.Footer>
</Modal>
);
}
export default Login;
In the developer tools looking at the React components tab, it is sending the correct information.
The props all seem to be there, the modal just doesn't seem to pop-up. Thanks

React Router history pushes to a path after form submission but does not render component

Every time I submit a form using axios, history.push updates the path in the browser but the component is not rendered. However, when I use BrowserRouter it works fine but apparently it doest support history.
App.js
import React from 'react';
import {Router, Route, Switch, BrowserRouter, withRouter} from 'react-router-dom';
import {history} from "./helpers";
import Layout from './containers/Layout'
import AllProductsList from "./containers/AllProducts";
import ProductAnalytics from "./containers/ProductAnalytics";
function App() {
return (
<>
<Router history={history}>
<Layout>
<Switch>
<Route path="/sell/allproducts" component={withRouter(AllProductsList)} />
<Route path="/sell/analytics/:product_id" component={withRouter(ProductAnalytics)} />
</Switch>
</Layout>
</Router>
</>
);
}
export default App;
Layout.js
import React from "react";
import Sidebar2 from "../components/sidebar2";
import Navbar2 from "../components/navbar2";
import Footer from "../components/Footer";
import {withRouter} from 'react-router-dom';
const Layout = ({children}) => {
return(
<div id="wrapper">
<Navbar2 />
<Sidebar2 />
<div className="content-page">
<div className="content">
{children}
</div>
</div>
<Footer />
</div>
);
};
export default withRouter(Layout);
Allproducts.js
const ProductCreateModal = (props) => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null);
const {
buttonLabel='Add New Product',
className
} = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const [productName, setProductName] = useState(null)
const [productPrice, setProductPrice] = useState(null)
const [productImage, setProductImage] = useState(null)
function handleSubmit(e) {
e.preventDefault();
setLoading(true);
console.log(productName)
console.log(productPrice)
console.log(productImage)
const formData = new FormData()
formData.append("name", productName)
formData.append("price", productPrice)
if(productImage) formData.append("image", productImage)
formData.append("status", "ACTIVE")
console.log(formData)
axios
.post(api.product.create, formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(res => {
console.log(res)
setLoading(false);
history.push('/sell/allproducts')
})
.catch(err => {
console.log(err)
setLoading(false);
setError(err.message || err)
})
}
return(
<>
<Button color="success" className="waves-effect waves-light mb-3" onClick={toggle}><i className="mdi mdi-cloud-upload mr-1"/>{buttonLabel}</Button>
<Modal isOpen={modal} toggle={toggle} className={className}>
<ModalHeader toggle={toggle}>Add New Product</ModalHeader>
<ModalBody>
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label for="exampleEmail">Name</Label>
<Input
type="text"
name="name"
id="exampleEmail"
placeholder="Product name"
value={productName}
onChange={e => setProductName(e.target.value)}
/>
</FormGroup>
<FormGroup>
<Label for="examplePassword">Price</Label>
<Input
type="number"
min="1"
step="any"
name="Price"
id="examplePassword"
placeholder="Product Price"
value={productPrice}
onChange={e => setProductPrice(e.target.value)}
/>
</FormGroup>
<FormGroup>
<Label for="exampleText">Description</Label>
<Input
type="textarea"
name="Description"
id="exampleText"
/>
</FormGroup>
<FormGroup>
<Label for="exampleFile">Image</Label>
<Input
type="file"
name="file"
id="exampleFile"
onChange={e => setProductImage(e.target.files[0])}
/>
<FormText color="muted">
This is some placeholder block-level help text for the above input.
It's a bit lighter and easily wraps to a new line.
</FormText>
</FormGroup>
<Button color='success' block>Submit</Button>
</Form>
</ModalBody>
</Modal>
</>
)
};
helpers/history.js
import {createBrowserHistory} from "history";
export const history = createBrowserHistory();
helpers/index.js
export * from "./history";
How do I get this working with Router and history.push?

how to redirect back to App with React.js from a component?

i'm new learner of React.js and i don't finding the right solution.
i have the CreateUser component and when a client success at creating one i want to Redirect the client to App...
i need it to happend in this function of CreateUser Component
private handleSubmit(e:any){
e.preventDefault();
this.setState({
username: this.state.username,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
userEmail: this.state.userEmail
})
this.passConfrim();
if (this.isAlertVisible){
console.log(this.state)
this.myUserDateService.create(this.state);
----> ** Right Here i need to redirect! ** <----
}
}
in the end of the function at the if statement
App:
import './App.css';
import { LoginComponent } from './components/LoginComponent';
import CreateUser from './components/CreateUser';
import "bootstrap/dist/css/bootstrap.min.css";
import { Router, Switch, Route, Link, useHistory as history} from "react-router-dom";
function App() {
return (
<Router history={history()} >
<nav className="navbar navbar-expand navbar-dark bg-dark">
<a href="/Home" className="navbar-brand">
To Do List
</a>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/Login"} className="nav-link">
Login
</Link>
</li>
<li className="nav-item">
<Link to={"/CreateUser"} className="nav-link">
Create User
</Link>
</li>
</div>
</nav>
<div id="App-content">
<Switch >
<Route exact path={["/", "/Home"]} />
<Route path="/Login" exact component={LoginComponent} />
<Route path="/CreateUser" exact component={CreateUser} />
</Switch>
</div>
</Router>
);
}
export default App;
CreateUser Component:
import React, { Component } from 'react';
import { UserDataService } from '../services/UserData.service';
interface IState {
username:string;
userEmail:string;
password:string;
confirmPassword:string;
}
export class CreateUser extends Component <{}, IState> {
isAlertVisible: boolean = true;
myUserDateService = new UserDataService();
constructor(props: {}, myUserDateService:UserDataService){
super(props );
this.state = {
username:"",
password:"",
confirmPassword:"",
userEmail:"",
}
}
private handleSubmit(e:any){
e.preventDefault();
this.setState({
username: this.state.username,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
userEmail: this.state.userEmail
})
this.passConfrim();
if (this.isAlertVisible){
console.log(this.state)
this.myUserDateService.create(this.state);
}
}
passConfrim(){
if(this.state.password !== this.state.confirmPassword){
this.isAlertVisible = false;
}else{
this.isAlertVisible = true;
}
}
render() {
return (
<div className="form-group">
<h1>Create User</h1>
<form onSubmit={e => this.handleSubmit(e)}>
<label >Username</label>
<input className="form-control" type="text" placeholder='Enter Username...' onChange={e => this.setState({username : e.target.value})} required/>
<br/>
<label >Email</label>
<input className="form-control" type="text" placeholder='Enter your email...' onChange={e => this.setState({userEmail : e.target.value})} required/>
<br/>
<label >Passowrd</label>
<input className="form-control" type="password" placeholder='Enter Password...' onChange={e => this.setState({password : e.target.value})} required/>
<br/>
<label >Confirm Passowrd</label>
<input className="form-control" type="password" placeholder='Confirm Password...' onChange={e => this.setState({confirmPassword : e.target.value })} required />
<div style={{color: "red", textAlign: "left"}} hidden={this.isAlertVisible}>**password not match</div>
<br/>
<button className="btn btn-primary" type="submit" >Create User</button>
</form >
</div>
)
}
}
export default CreateUser;
Basically you need not pass history with Router instead you can use withRouter high order component from react-router.
Import withRouter inside createUser component - https://reacttraining.com/react-router/core/api/withRouter
import { withRouter } from "react-router";
Then we just need to export CreateUser component like -
export default withRouter(CreateUser);
Now you have access to all props related to routing inside CreateUser component, now you can use -
this.props.history.push('/your-route');
To check what else properties you have with withRouter, you can just console.log this.props.history inside CreateUser component.
Tip - You cannot use hooks inside class components, so you cannot use useHistory inside CreateUser component instead use withRouter.
you can use history.push('/yourRoute') and that will take you to whatever route your heart so desires
Since you are extending the user component from react, it is a class component and you cannot use 'useHistory' hooks inside it.
Also you are passing history as a prop to router, can you try the below code to navigate and let me know.
this.props.history.push('/yourroute');

How to make controlled input component in Redux?

I'm implementing movie search functionality using the moviedb api. I have implemented in React only but I want to do it in Redux. Here is my approach in React.
Header.js
import React, { Component } from "react"
import { Navbar, Form, FormControl } from "react-bootstrap"
import { NavLink } from "react-router-dom"
import axios from "axios"
import MovieCards from "./MovieCards"
const apiKey = process.env.REACT_APP_MOVIE_DB_API_KEY
class Header extends Component {
state = {
isSearching: false,
value: "",
movies: []
}
searchMovies = async val => {
this.setState({ isSearching: true })
const res = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${val}&page=1&include_adult=true`
)
const movies = await res.data.results
this.setState({ movies: movies, isSearching: false })
}
handleChange = e => {
const { name, value } = e.target
this.searchMovies(value)
this.setState({
[name]: value
})
}
render() {
return this.state.value === "" ? (
<div>
<Navbar
bg="dark"
expand="lg"
style={{ justifyContent: "space-around" }}
>
<NavLink to="/">
<Navbar.Brand>Movie Catalogue</Navbar.Brand>
</NavLink>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Form inline>
<FormControl
type="text"
placeholder="Search"
className="mr-sm-2"
onChange={this.handleChange}
name="value"
value={this.state.value}
/>
</Form>
</Navbar.Collapse>
<NavLink to="/popular">Popular</NavLink>
<NavLink to="/now-playing">Now Playing</NavLink>
<NavLink to="/top-rated">Top Rated</NavLink>
<NavLink to="/upcoming">Upcoming</NavLink>
</Navbar>
{this.state.movies.map((movie, i) => {
return <MovieCards key={i} movie={movie} />
})}
</div>
) : (
<div>
<Navbar
bg="dark"
expand="lg"
style={{ justifyContent: "space-around" }}
>
<NavLink to="/">
<Navbar.Brand>Movie Catalogue</Navbar.Brand>
</NavLink>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Form inline>
<FormControl
type="text"
placeholder="Search"
className="mr-sm-2"
onChange={this.handleChange}
name="value"
value={this.state.value}
/>
</Form>
</Navbar.Collapse>
<p style={{ color: "white" }}>
Search results for " {this.state.value} "
</p>
</Navbar>
{this.state.movies.map((movie, i) => {
return <MovieCards key={i} movie={movie} />
})}
</div>
)
}
}
export default Header
I want to do it using Redux, so I'm doing it this way.
Header.js
import React, { Component } from "react"
import { Navbar, Form, FormControl } from "react-bootstrap"
import { NavLink } from "react-router-dom"
import axios from "axios"
import { connect } from "react-redux"
import { movieSearch } from "../actions/index"
import MovieCards from "./MovieCards"
const apiKey = process.env.REACT_APP_MOVIE_DB_API_KEY
class Header extends Component {
handleChange = e => {
const { name, value } = e.target
this.props.dispatch(movieSearch(value)) // I'm not sure if this is the right approach. I'm dispatching and then setting state.
this.setState({
[name]: value
})
}
render() {
return this.state.value === "" ? (
<div>
<Navbar
bg="dark"
expand="lg"
style={{ justifyContent: "space-around" }}
>
<NavLink to="/">
<Navbar.Brand>Movie Catalogue</Navbar.Brand>
</NavLink>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Form inline>
<FormControl
type="text"
placeholder="Search"
className="mr-sm-2"
onChange={this.handleChange}
name="value"
value={this.state.value}
/>
</Form>
</Navbar.Collapse>
<NavLink to="/popular">Popular</NavLink>
<NavLink to="/now-playing">Now Playing</NavLink>
<NavLink to="/top-rated">Top Rated</NavLink>
<NavLink to="/upcoming">Upcoming</NavLink>
</Navbar>
{this.state.movies.map((movie, i) => {
return <MovieCards key={i} movie={movie} />
})}
</div>
) : (
<div>
<Navbar
bg="dark"
expand="lg"
style={{ justifyContent: "space-around" }}
>
<NavLink to="/">
<Navbar.Brand>Movie Catalogue</Navbar.Brand>
</NavLink>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Form inline>
<FormControl
type="text"
placeholder="Search"
className="mr-sm-2"
onChange={this.handleChange}
name="value"
value={this.state.value}
/>
</Form>
</Navbar.Collapse>
<p style={{ color: "white" }}>
Search results for " {this.state.value} "
</p>
</Navbar>
{this.state.movies.map((movie, i) => {
return <MovieCards key={i} movie={movie} />
})}
</div>
)
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps)(Header)
actions/index.js
export const movieSearch = val => {
const movieSearchUrl = `https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${val}&page=1&include_adult=true`
return async dispatch => {
dispatch({ type: "SEARCHING_MOVIES_START" })
try {
const res = await axios.get(movieSearchUrl)
dispatch({
type: "SEARCHING_MOVIES_SUCCESS",
data: { searchResults: res.data.results }
})
} catch (err) {
dispatch({
type: "SEARCHING_MOVIES_FAILURE",
data: { error: "Could not find the movie" }
})
}
}
}
reducers/movieSearchReducer.js
const initialState = {
value: "",
isSearchingMovies: false,
isSearchedMovies: false,
movieList: [],
searchingError: null
}
export const movieSearchReducer = (state = initialState, action) => {
switch (action.type) {
case "SEARCHING_MOVIES_START":
return {
...state,
isSearchingMovies: true,
searchingError: null
}
case "SEARCHING_MOVIES_SUCCESS":
return {
...state,
isSearchingMovies: false,
isSearchedMovies: true,
movieList: action.data,
searchingError: null
}
case "SEARCHING_MOVIES_FAILURE":
return {
...state,
searchingError: action.data.error
}
}
}
I'm not sure how to implement the part of the below input form part in Redux. Please help if you can.
onChange={this.handleChange}
name="value"
value={this.state.value}
When you change from state in component to redux, you will generally remove the react state and pickup the redux state from the 'props'.
So step 1 is to get rid of your setState all together.
value={this.state.value}
will become
value={this.props.movieList}
In order to get the movieList in the props, you need to wrap your component in a 'container' and use mapStateToProps to map the redux state to your props.
See https://react-redux.js.org/api/connect for more details
If you use Redux to store the movies, you can delete the local state to your component, and use redux's movieList prop instead.

"store" not found in either the context or props of "Connect(Signup)

Am trying to run enzyme tests but am facing this error,
Invariant Violation: Could not find "store" in either the context or
props of "Connect(Signup)". Either wrap the root component in a
, or explicitly pass "store" as a prop to "Connect(Signup)".
I used Redux in my application and it runs well in the browser but fails to run tests in enzyme and jest.
Below is my code :
For the test file below is the code in App.test.js.
import React from 'react';
import ReactDOM from 'react-dom';
import App from '../App'
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
Signup.js
import React, { Component } from 'react';
import NavBar from './subcomponents/NavBar'
import SignUpForm from './subcomponents/SignUpForm'
import { connect } from 'react-redux'
import {userSignUpRequest} from "./actions/signUpActions";
import PropTypes from 'prop-types';
class Signup extends Component {
render() {
const {userSignUpRequest} = this.props;
return (
<div>
<NavBar/>
<SignUpForm userSignUpRequest={userSignUpRequest}/>
</div>
);
}
}
Signup.propTypes = {
userSignUpRequest: PropTypes.func.isRequired
}
export default connect(null, { userSignUpRequest })(Signup);
SignUpForm.js
import React, {Component} from 'react';
import {Input, Icon, Row, Card, Button, ProgressBar, Col, Preloader} from 'react-materialize'
import '../css/signup.css'
import PropTypes from 'prop-types';
class SignUpForm extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
email:'',
password:'',
confirm_password:'',
visible: true
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const {name,value} = event.target;
this.setState({[name]: value});
}
handleSubmit(event) {
event.preventDefault();
console.log('my state ', this.state);
this.setState({visible: true});
if(this.state.password.trim() === this.state.confirm_password.trim()) {
this.props.userSignUpRequest(this.state);
}
else{
alert('No they don\'t match');
this.setState({visible: true});
}
}
componentDidMount() {
console.log('Parent did mount.');
document.getElementById('text_message').style.visibility = "hidden";
this.setState({visible: false});
}
render() {
const isVisible = this.state.visible;
return (
<div>
<Card className="card-effects right">
{isVisible && <ProgressBar id="progress_Bar" name="progress_Bar"/>}
<Row>
<label className="text-message" id="text_message" name="text_message"><i>text message</i></label>
</Row>
<form className="card-form-signup" onSubmit={this.handleSubmit}>
<Row>
<label className="signup-header"><b>Signup to Authors Haven</b></label>
</Row>
<Row>
<Input s={12} placeholder="Username" name="username" value={this.state.username} onChange={this.handleChange} validate>
<Icon className="icon-styles">account_box</Icon></Input>
</Row>
<Row>
<Input s={12} type='text' name="email" value={this.state.email} onChange={this.handleChange} placeholder="Email" validate><Icon className="green darken-4">email</Icon></Input>
</Row>
<Row>
<Input s={12} type='password' name="password" placeholder="Password" value={this.state.password} onChange={this.handleChange} validate>
<Icon className="icon-styles">vpn_key</Icon></Input>
</Row>
<Row>
<Input s={12} type='password' name="confirm_password" placeholder="Confirm password" value={this.state.confirm_password} onChange={this.handleChange} validate>
<Icon className="icon-styles">vpn_key</Icon></Input>
</Row>
<Row>
<label >Already have an account ? </label>
</Row>
<Row>
<Button className='button-effects' type="submit" value="Submit" > Sign up </Button>
</Row>
</form>
</Card>
</div>
);
}
}
SignUpForm.propTypes = {
userSignUpRequest: PropTypes.func.isRequired
}
export default SignUpForm;
In test you'll still need to mount the components inside a <Provider> so that all usages of connect() downstream of App can work properly.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, div);

Resources