1
votes

Got these warnings:

Warning: Failed propType: Required prop emailExists was not specified in SignUp

Warning: Failed propType: Required prop onEmailChange was not specified in SignUp

emailExists prop and onEmailChange prop are not specified in components/SignUp.js. I guess mapStateToProps and mapDispatchToProps passed into connect() in containers/SignUp.js is not injecting the props into components/SignUp.js due to some misconfiguration.

index.js:

import React from 'react'
import {render} from 'react-dom'
import {Provider} from 'react-redux'
import {createStore, applyMiddleware} from 'redux'
import {Router, Route, IndexRoute, browserHistory} from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import createLogger from 'redux-logger'
import thunkMiddleware from 'redux-thunk'
import donrollApp from './reducers'
import App from './components/LoginApp'
import Login from './components/Login'
import SignUp from './components/SignUp'

const loggerMiddleware = createLogger()

let store = createStore(donrollApp, applyMiddleware(thunkMiddleware, loggerMiddleware))
const history = syncHistoryWithStore(browserHistory, store)
render(
    <Provider store={store}>
        <Router history={history}>
            <Route path="/" component={App}>
                <IndexRoute component={Login}/>
                <Route path="signup" component={SignUp}/>
                <Route path="*" component={Login}/>
            </Route>
        </Router>
    </Provider>,
    document.getElementById('root')
);

containers/SignUp.js:

import { connect } from 'react-redux'
import SignUp from '../components/SignUp'
import { fetchEmailExists } from '../actions'

const mapStateToProps = (state, ownProps) => {
    return {
        emailExists: state.SignUp.emailExists
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onEmailChange: (email) => {
            dispatch(fetchEmailExists(email))
        }
    }
}

const SignUpContainer = connect(
    mapStateToProps,
    mapDispatchToProps
)(SignUp)

export default SignUpContainer

reducers/SignUp.js:

import Immutable from 'immutable'

const SignUp = (state={emailExists:false, isCheckingEmail: false}, action) => {
    let newState = Immutable.Record(state);
    switch (action.type) {
        case 'CHECK_EMAIL_EXISTS_REQUEST':
            return (new newState({isCheckingEmail:true})).toJS();
        case 'CHECK_EMAIL_EXISTS_RESPONSE':
            return (new newState({emailExists: action.emailExists})).toJS();
        default:
            return state
    }
}

export default SignUp

reducers/index.js:

import { combineReducers } from 'redux'
import SignUp from './SignUp'
import {  routerReducer } from 'react-router-redux'

const donrollApp = combineReducers({
    SignUp,
    routing: routerReducer
})

export default donrollApp

components/SignUp.js:

import React, { PropTypes }  from 'react'
import {Link} from 'react-router'

const SignUp = ({emailExists, onEmailChange}) => {
    let signupData = {
        firstname:{},
        lastname:{},
        email:{},
        username:{},
        password:{},
        confirmPassword:{}
    }
    return (
        <div>
            <form>
                <div className="form-group row">
                    <h4 className="col-sm-12">Sign Up</h4>
                </div>
                <div className="form-group row">
                    <label htmlFor="inputFirstname3" className="col-sm-3 col-form-label">Firstname</label>
                    <div className="col-sm-9">
                        <input type="text" className="form-control" id="inputFirstname3" placeholder="Firstname" ref={node=>{signupData.firstname=node;}} />
                    </div>
                </div>
                <div className="form-group row">
                    <label htmlFor="inputLastname3" className="col-sm-3 col-form-label">Lastname</label>
                    <div className="col-sm-9">
                        <input type="text" className="form-control" id="inputLastname3" placeholder="Lastname" ref={node=>{signupData.lastname=node;}}/>
                    </div>
                </div>
                <div className={emailExists?'form-group row has-danger':'form-group row'}>
                    <label htmlFor="inputEmail3" className="col-sm-3 col-form-label">Email</label>
                    <div className="col-sm-9">
                        <input type="email" onBlur={e=>onEmailChange(signupData.email.value)} className="form-control" id="inputEmail3" placeholder="Email" ref={node=>{signupData.email=node;}}/>
                        {emailExists?<div className="form-control-feedback">Shit, that email's taken. Try another?</div>:null}
                    </div>
                </div>
                <div className="form-group row">
                    <label htmlFor="inputUsername3" className="col-sm-3 col-form-label">Username</label>
                    <div className="col-sm-9">
                        <input type="text" className="form-control" id="inputUsername3" placeholder="Username" ref={node=>{signupData.username=node;}}/>
                    </div>
                </div>
                <div className="form-group row">
                    <label htmlFor="inputPassword3" className="col-sm-3 col-form-label">Password</label>
                    <div className="col-sm-9">
                        <input type="password" className="form-control" id="inputPassword3" placeholder="Password" ref={node=>{signupData.password=node;}}/>
                    </div>
                </div>
                <div className="form-group row">
                    <label htmlFor="inputConfirmPassword3" className="col-sm-3 col-form-label">Confirm Password</label>
                    <div className="col-sm-9">
                        <input type="password" className="form-control" id="inputConfirmPassword3"
                               placeholder="Confirm Password" ref={node=>{signupData.confirmPassword=node;}}/>
                    </div>
                </div>
                <div className="form-group row">
                    <div className="offset-sm-3 col-sm-9">
                        <button type="submit" className="btn btn-primary">Sign Up</button>
                        {" "}
                        <Link to="/">Login</Link>
                    </div>
                </div>
            </form>
        </div>
    )
}

SignUp.propTypes = {
    emailExists: PropTypes.bool.isRequired,
    onEmailChange: PropTypes.func.isRequired
}

export default SignUp
1
you can simply write mapDispatchToProps like this. const SignUpContainer = connect( mapStateToProps, {fetchEmailExists: onEmailChange} )(SignUp). And regarding the other prop emailExists, is the mapStateToProps throwing any error?Deadfish
I tried your connect() statement. It didn't solve the problem. Normally, mapStateToProps doesn't throw any errors, instead the above mentioned warnings are thrown from SignUp component. But when I change the value in email input box, then a onEmailChange is not a function error is thrown. Here, the onBlur event is handled by calling the onEmailChange function, which is not specified.Kuppuswamy
I found the problem. I imported the signup component instead of signup container and passed it directly into the render function in index.js. I have posted an answer with the solution below.Kuppuswamy

1 Answers

0
votes

The problem is, I imported components/SignUp.js instead of containers/SignUp.js in index.js. So the line import SignUp from './components/SignUp' in index.js becomes import SignUp from './containers/SignUp'. Because, containers form a wrap around the components and map the Redux states to React props and map the Redux dispatches to React prop callbacks with all the props injected into the components when needed. The containers subscribe to Redux store and inject new props to the components when new subscription signals arrive and the components update themselves with the new props. The components invoke the callback props to change the data and in-turn make the containers to dispatch actions. For more details, we have the documentation here.