Search code examples
javascriptreactjsformsconditional-formatting

Conditional form rendering in react


So I am trying to render a form to start a new darts game conditionally depending on the game type selected. The form has a local state and "knows" which game is selected. So when selecting game "X01" I need a variant, inCondition and outCondition Dropdown whereas the game "Cricket" just needs one additional dropdown for variant (other values than x01 variants). I started to design a game form which looks like this:

gameform.js

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import SelectInputMultiple from '../common/SelectInputMultiple';
import SelectInput from '../common/SelectInput';
import { games, x01Variants, conditions, cricketVariants } from './assets';

export default class GameForm extends Component {
  constructor(props) {
    super(props);
    this.players = props;
    this.handleMultipleChange = this.handleMultipleChange.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  state = {
    selectedPlayers: [],
    game: 'x01',
    x01variant: '501',
    inCondition: 'straight',
    outCondition: 'double',
    cricketVariant: 'cutthroat',
    errors: {}
  };

  formIsValid() {
    const _errors = {};
    if (this.state.selectedPlayers.length === 0)
      _errors.selectedPlayers = 'You need to select at least one player';

    this.setState({
      errors: _errors
    });

    return Object.keys(_errors).length === 0;
  }

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  handleMultipleChange = e => {
    let _selectedPlayers = [...e.target.options]
      .filter(o => o.selected)
      .map(o => o.value);

    this.setState(prevState => ({
      selectedPlayers: { ...prevState.selectedPlayers, _selectedPlayers }
    }));
  };

  handleSubmit = e => {
    e.preventDefault();
    if (!this.formIsValid()) return;
    let _game = {
      selectedPlayers: this.state.selectedPlayers,
      game: this.state.game,
      x01Variant: this.state.x01variant,
      inCondition: this.state.inCondition,
      outCondition: this.state.outCondition,
      cricketVariant: this.state.cricketVariant
    };
    this.props.onSubmit(_game);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <SelectInputMultiple
          id="players"
          label="Players"
          name="players"
          onChange={this.handleMultipleChange}
          options={this.props.players}
          error={this.state.errors.selectedPlayers}
        />
        <SelectInput
          id="game"
          label="Game Type"
          name="game"
          onChange={this.handleChange}
          options={games}
          value={this.state.game}
          error={this.state.errors.game}
        />
        <SelectInput
          id="x01Variant"
          label="X01 Variants"
          name="x01Variant"
          onChange={this.handleChange}
          options={x01Variants}
          value={this.state.x01variant}
          error={this.state.errors.x01Variants}
        />
        <SelectInput
          id="inCondition"
          label="In Condition"
          name="inCondition"
          onChange={this.handleChange}
          options={conditions}
          value={this.state.inCondition}
          error={this.state.errors.condition}
        />
        <SelectInput
          id="outCondition"
          label="Out Condition"
          name="outCondition"
          onChange={this.handleChange}
          options={conditions}
          value={this.state.outCondition}
          error={this.state.errors.condition}
        />
        <SelectInput
          id="cricketVariant"
          label="Variant"
          name="cricketVariant"
          onChange={this.handleChange}
          options={cricketVariants}
          value={this.state.cricketVariant}
          error={this.state.errors.cricketVariant}
        />
        <input type="submit" value="Start Game" className="btn btn-primary" />
      </form>
    );
  }
}

GameForm.propTypes = {
  onSubmit: PropTypes.func.isRequired,
  players: PropTypes.array
};

So my goal is to just show the corresponding fields linked to the game type. How can I do that depending on this.state.game?

Thanks in advance for any hint!


Solution

  • You could just conditionally render the variant select input when the game mode is set to "Cricket"

    {this.state.game === 'Cricket' && (
      <SelectInput />
    )}

    The reason you can write it as such is because in JavaScript the && operator basically returns the first value if it is falsy and the second value if the first value is truthy. i.e. a && b will return 'a' if 'a' is falsy, and it will return 'b' if 'a' is truthy. So in this case when this.state.game === 'Cricket' then the JSX code will be returned hence rendering the form input.

    One additional tip! If you want to render one of two JSX elements based on a condition, you can just use a ternary expression!

    {this.state.game === 'Cricket' ? (
      // Renders when game mode is 'Cricket'
      <Cricket />
    ) : (
      // Renders when game mode is NOT 'Cricket'
      <SomeOtherGame />
    )}