maximus
maximus

Reputation: 43

How to return user input in react?

I am new to React and coding in general, and I am trying to build this simple flashcard app to get a better understanding of react. I know this code is very bad and it should probably be broken into more components, but I can't figure out how to add an event handler to the submit button that will create a flashcard that will have the definition on one side and the term on the other and will make it so when the card is clicked it will change from definition to term. Any feedback is appreciated and I apologize in advance if this question is dumb.

class flashcard extends Component {
  constructor(props) {
    super(props);
    this.state = {
      definition: "",
      term: "",
    };
  }
  createCard = () => {
    this.setState({
      definition: "test",
    });
  };

  render() {
    return (
      <div>
        <Navbar bg="primary" variant="dark">
          <Navbar.Brand>Flashcard</Navbar.Brand>
          <Nav className="mr-auto">
            <Button style={this.buttonstyles} variant="outline-light">
              Add Card
            </Button>
          </Nav>
        </Navbar>
        <Card style={this.styles}>
          <Card.Img variant="top" />
          <Card.Body>
            <Form>
              <Form.Group controlId="formBasicEmail">
                <Form.Label>definition</Form.Label>
                <Form.Control type="definition" placeholder="definition" />
              </Form.Group>

              <Form.Group controlId="formBasicPassword">
                <Form.Label>term</Form.Label>
                <Form.Control type="term" placeholder="term" />
              </Form.Group>
              <Button variant="primary" type="submit" onClick={this.createCard}>
                Submit
              </Button>
            </Form>
          </Card.Body>
        </Card>
        <Button style={this.previoussytle} variant="primary">
          Previous
        </Button>
        <Button style={this.nextsytle} className="m-2" variant="primary">
          Next
        </Button>
      </div>
    );
  }

Upvotes: 3

Views: 423

Answers (2)

Artemis Prime
Artemis Prime

Reputation: 183

As the commentators mention, you do not need a Form in this case. You simply need to update the array in state form the current values of the inputs. Mixing submit and an onClick() handler is rarely a good idea.

@qudrat gave you a nice starting point in his codesandbox. It's more idiomatic to React and a good model.

Upvotes: 2

Juan Marco
Juan Marco

Reputation: 3241

You can build this in many ways, with or without a form. I chose to use a form and controlled components to handle <input> elements.

With this layout, we would need to keep track of three items for each card:

  • term (string)
  • definition (string)
  • flipped (boolean)

All three items would be inside a card object that would look something like this:

{
  term: 'vcs',
  definition: 'version control system',
  flipped: false
}

We also need an empty array to store all the cards. So the initial state of the app will look like this:

state = {
  term: "",
  definition: "",
  cards: []
};

The term and definition are updated using handleChange event handler.

When the term and definition is submitted (via handleSubmit), the card will be added to the cards array.

The event handler handleClick will handle the toggling of the flipped property of each card.

We can then use conditional rendering to display the term or definition for each card.

{c.flipped ? c.definition : c.term}

Working example:

class App extends React.Component {
  state = {
    term: "",
    definition: "",
    cards: []
  };
  
  // Set state for input elements using "Computed property names"
  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  // Toggle each card
  handleClick = index => {
    const updatedCards = this.state.cards;
    updatedCards[index].flipped = !updatedCards[index].flipped;
    this.setState({
      cards: updatedCards
    });
  };
  
  // Add a card to the array, then clear input elements
  handleSubmit = e => {
    e.preventDefault();
    this.setState(state => {
      return {
        cards: [
          ...state.cards,
          { term: state.term, definition: state.definition, flipped: false }
        ],
        term: '',
        definition: ''
      };
    });
  };

  render() {
    return (
      <React.Fragment>
        <form className="card-form" onSubmit={this.handleSubmit}>
          <input
            type="text"
            name="term"
            onChange={this.handleChange}
            placeholder="term"
            value={this.state.term}
          />

          <input
            type="text"
            name="definition"
            onChange={this.handleChange}
            placeholder="definition"
            value={this.state.definition}
          />
          <button>Add</button>
        </form>
        <div className="card-grid">
          {this.state.cards.map((c, i) => (
            <div
              key={c.term + i}
              className="card"
              onClick={() => this.handleClick(i)}
            >
              {c.flipped ? c.definition : c.term}
            </div>
          ))}
        </div>
      </React.Fragment>
    );
  }
}

ReactDOM.render(<App/>, document.getElementById('root'));
body {
  font-family: Arial, Helvetica, sans-serif;
}

.card-grid {
  padding: 0.2rem;
  display: flex;
  flex-wrap: wrap;
}

.card {
  border: 2px solid gray;
  border-radius: 4px;
  padding: 10px;
  width: 80px;
  height: 90px;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Note: To keep the example simple I did not include React Bootstrap, but you shouldn't have too much problems implementing it in your final project.

Upvotes: 2

Related Questions