React

Creating Forms with React & Redux

Learn how to build complex forms that create and update data locally as well as on a server

PRO

Next, let's build out the Registration and Settings form components. The Settings component will allow a user to modify their profile, while the Registration component will allow a user to, well, register!

Registration View

First off, let's add a register function to agent.js:

Add the register function to the Auth constant

src/agent.js

// ...

const Auth = {
  current: () =>
    requests.get('/user'),
  login: (email, password) =>
    requests.post('/users/login', { user: { email, password } }),
+ register: (username, email, password) =>
+   requests.post('/users', { user: { username, email, password } })
};

// ...

Now, we need to create the corresponding reducers. First, we'll update the auth state in the same way that the login reducer does.

Update the auth reducer to handle the REGISTER action type

reducers/auth.js

export default (state = {}, action) => {
  switch (action.type) {
    case 'LOGIN':
+   case 'REGISTER':
      return {
        ...state,
        inProgress: false,
        errors: action.error ? action.payload.errors : null
      };
  // ...
};

Now that we can handle any registration errors, we'll need to handle the JWT received from when a user is registered.

Add the REGISTER action type to the common reducer

reducers/common.js

export default (state = defaultState, action) => {
  switch (action.type) {
    // ...
    case 'LOGIN':
+   case 'REGISTER':
      return {
        ...state,
        redirectTo: action.error ? null : '/',
        token: action.error ? null : action.payload.user.token,
        currentUser: action.error ? null : action.payload.user
      };
  }
  return state;
};

You can see that login and register are similar: when the action succeeds, we redirect the user to the main view and save the JWT and current user in state. Now let's build out the Registration component. This is a lot of code, but you'll notice that it is similar to how we build out the Login component.

Create the Register component

components/Register.js

import { Link } from 'react-router';
import ListErrors from './ListErrors';
import React from 'react';
import agent from '../agent';
import { connect } from 'react-redux';

const mapStateToProps = state => ({ ...state.auth });

const mapDispatchToProps = dispatch => ({
  onChangeEmail: value =>
    dispatch({ type: 'UPDATE_FIELD_AUTH', key: 'email', value }),
  onChangePassword: value =>
    dispatch({ type: 'UPDATE_FIELD_AUTH', key: 'password', value }),
  onChangeUsername: value =>
    dispatch({ type: 'UPDATE_FIELD_AUTH', key: 'username', value }),
  onSubmit: (username, email, password) => {
    const payload = agent.Auth.register(username, email, password);
    dispatch({ type: 'REGISTER', payload })
  }
});

class Register extends React.Component {
  constructor() {
    super();
    this.changeEmail = event => this.props.onChangeEmail(event.target.value);
    this.changePassword = event => this.props.onChangePassword(event.target.value);
    this.changeUsername = event => this.props.onChangeUsername(event.target.value);
    this.submitForm = (username, email, password) => event => {
      event.preventDefault();
      this.props.onSubmit(username, email, password);
    }
  }

  render() {
    const { email, username, password } = this.props;

    return (
      <div className="auth-page">
        <div className="container page">
          <div className="row">

            <div className="col-md-6 offset-md-3 col-xs-12">
              <h1 className="text-xs-center">Sign Up</h1>
              <p className="text-xs-center">
                <Link to="login">
                  Have an account?
                </Link>
              </p>

              <ListErrors errors={this.props.errors} />

              <form onSubmit={this.submitForm(username, email, password)}>
                <fieldset>

                  <fieldset className="form-group">
                    <input
                      className="form-control form-control-lg"
                      type="text"
                      placeholder="Username"
                      value={this.props.username}
                      onChange={this.changeUsername} />
                  </fieldset>

                  <fieldset className="form-group">
                    <input
                      className="form-control form-control-lg"
                      type="email"
                      placeholder="Email"
                      value={this.props.email}
                      onChange={this.changeEmail} />
                  </fieldset>

                  <fieldset className="form-group">
                    <input
                      className="form-control form-control-lg"
                      type="password"
                      placeholder="Password"
                      value={this.props.password}
                      onChange={this.changePassword} />
                  </fieldset>

                  <button
                    className="btn btn-lg btn-primary pull-xs-right"
                    type="submit"
                    disabled={this.props.inProgress}>
                    Sign in
                  </button>

                </fieldset>
              </form>
            </div>

          </div>
        </div>
      </div>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Register);

In order to see our Registration component, we need to update the router.

Add the Registration route

src/index.js

[...]

import Home from './components/Home';
import Login from './components/Login';
+ import Register from './components/Register';

[...]

<Provider store={store}>
    <Router history={hashHistory}>
      <Route path="/" component={App}>
        <IndexRoute component={Home} />
        <Route path="login" component={Login} />
+       <Route path="register" component={Register} />
      </Route>
    </Router>
  </Provider>

Now our Register component is wired up! Try it out and see if you can register for a new account.