/ app / js / components / Search.js
Search.js
  1  import React, { Component } from 'react';
  2  import { withRouter } from 'react-router-dom';
  3  import { FormGroup, InputGroup, FormControl, Button, Glyphicon } from 'react-bootstrap';
  4  
  5  /**
  6   * Class that renders a form to faciliate searching of
  7   * for a username and to redirect to that user's tweets
  8   * page.
  9   * 
 10   * @extends React.Component
 11   */
 12  class Search extends Component {
 13  
 14    //#region Constructor
 15    constructor(props, context) {
 16      super(props, context);
 17  
 18      // initial state
 19      this.state = {
 20        username: '',
 21        usernameHasChanged: false
 22      };
 23    }
 24    //#endregion
 25  
 26    //#region Component events
 27    /**
 28     * Handles the 'Search' button click event which
 29     * sends the browser to the route to see the specified
 30     * user's tweets.
 31     * 
 32     * @returns {null}
 33     */
 34    _handleClick(e) {
 35      // if the form is in error, or the user has not typed in a username, do no nothing
 36      if (this._getValidationState() === 'error' || !this.state.usernameHasChanged) {
 37        return e.preventDefault();
 38      }
 39  
 40      // redirec the user to the user's tweets page
 41      this.props.history.push('/@' + this.state.username);
 42    }
 43  
 44    /**
 45     * When user changes an input value, record that in the state.
 46     * 
 47     * @param {SyntheticEvent} cross-browser wrapper around the browser’s native event
 48     * 
 49     * @return {null}
 50     */
 51    _handleChange(e) {
 52      let state = { usernameHasChanged: true };
 53      state[e.target.name] = e.target.value;
 54      this.setState(state);
 55    }
 56    //#endregion
 57  
 58    //#region Helper methods
 59    /**
 60     * Validates the form. Return null for no state change,
 61     * 'success' if valid.
 62     * 
 63     * @return {string} null for no state change, 'success' 
 64     * if valid.
 65     */
 66    _getValidationState() {
 67  
 68      // ensure that the username has been changed and that a user has typed something in
 69      return (this.state.username === '' && !this.state.usernameHasChanged) || this.state.username.length > 0 ? null : 'error';
 70    }
 71    //#endregion
 72  
 73    //#region React lifecycle events
 74    render() {
 75      let validationState = this._getValidationState();
 76      let isValid = validationState !== 'error';
 77  
 78      return (
 79        <FormGroup validationState={validationState}>
 80          <InputGroup>
 81            <FormControl 
 82              type="text"
 83              value={this.state.username}
 84              placeholder="username"
 85              name="username"
 86              onChange={ (e) => this._handleChange(e) }
 87              onKeyPress={ (e) => e.key === 'Enter' ? this._handleClick(e) : false }
 88            />
 89            <InputGroup.Button>
 90            <Button
 91              bsStyle="primary"
 92              disabled={ !isValid }
 93              onClick={ !isValid ? null : (e) => this._handleClick(e) }><Glyphicon glyph="search" /></Button>
 94            </InputGroup.Button>
 95          </InputGroup>
 96        </FormGroup>
 97      );
 98    }
 99    //#endregion
100  }
101  
102  export default withRouter(Search);