Reputation: 43
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
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
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:
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