Reputation:
I am a few days into learning React JS. It's been very interesting and simplifies JavaScript, at least how I view it. My goal at the moment is to create a button that when clicked will create a new card with a random number generated in the card.
I can't figure out how to add the onClick= functionality for it to work the way I'm envisioning it. At the moment I have the code for the button written on my index.js file. index.js is my main file, it is where my App component is located. I see people use a file named App.js, not sure why, but that's another question for later.
Currently my buttons are stylized the following way.
const App = () => {
return (
<body>
<header>
<div className="ui buttons">
<button className="ui button mb-1 mt-1">
<i className="plus icon"></i>
Add Card
</button>
<div className="or mb-1 mt-1"></div>
<button className="ui positive button mb-1 mt-1">
<i className="sort numeric down icon"></i>
Sort All
</button>
</div>
</header>
They are located in a header container at the top of my page. It looks like this following image.
I want to be able to click the Add Card button and then for it to create a new card in my card container. As picture below.
The current card you see in the above image is hard coded at the moment. I created a MainCard.js component that houses the code for the card including the functionality of the randomly generated number and the styling.
import "bootstrap/dist/css/bootstrap.css";
import React from "react";
import { Card, Button } from "react-bootstrap";
let getRandomNumber = function (min, max) {
let getRandom = Math.floor(Math.random() * max + min);
return getRandom;
};
const MainCard = () => {
return (
<Card>
<Button
className="ui mini red basic icon button"
style={{
position: "absolute",
top: "0",
right: "0",
}}
>
<i
className="red x icon"
style={{
position: "relative",
top: "0",
right: "0",
}}
></i>
</Button>
<Card.Body>{getRandomNumber(0, 101)}</Card.Body>
</Card>
);
};
export default MainCard;
Previously I was doing the onClick functionality in my AddCard.js but for some reason the Alert I created was both being generated when the page was being reloaded as well as when the button was clicked. I couldn't figure out the reasoning for this and it has become a wall that I haven't been able to climb over yet. I'm pretty sure I'm missing something simple, but I just can't figure out what that is. Any ideas how I can achieve this?
Upvotes: 1
Views: 9015
Reputation: 81
As stated on Charles Bamford's response, you'll have to use the useState method. The useState method creates a variable and a method to set the value of this variable. Everytime you change a state (using it's setter) React will check the code and re-render everywhere it was used. You will need something like this:
const App = () => {
const [cards, setCards] = useState([]); // instantiate cards as a empty Array
const addCard = () => {
// create a new array with the old values and the new random one
const newCards = [...cards, Math.floor(Math.random() * 100)];
setCards(newCards);
};
const removeCard = (cardIndex) => {
// create a new array without the item that you are removing
const newCards = cards.filter((card, index) => index !== cardIndex);
setCards(newCards);
};
return (
<body>
<header>
<div className="ui buttons">
<button className="ui button mb-1 mt-1" onClick={() => addCard()}>
<i className="plus icon"></i>
Add Card
</button>
<div className="or mb-1 mt-1"></div>
<button
className="ui positive button mb-1 mt-1"
onClick={() => addCard()}
>
<i className="sort numeric down icon"></i>
Sort All
</button>
</div>
</header>
<main>
{cards.map((cardNumber, index) => (
<MainCard number={cardNumber} onRemove={() => removeCard(index)} />
))}
</main>
</body>
);
};
With an array like this [10, 15, 33], the cards.map will do something like this for you:
<main>
<MainCard number={cards[0]} onRemove={() => removeCard(0)} /> // 10
<MainCard number={cards[1]} onRemove={() => removeCard(1)} /> // 15
<MainCard number={cards[2]} onRemove={() => removeCard(2)} /> // 33
</main>
We are passing the "number" and the "onRemove" function from the App component to the MainCard component. Now we just have to get it from our props, like this:
const MainCard = (props) => {
const { onRemove, number } = props
return (
<Card>
<Button
className="ui mini red basic icon button"
style={{
position: "absolute",
top: "0",
right: "0",
}}
onClick={() => onRemove()}
>
<i
className="red x icon"
style={{
position: "relative",
top: "0",
right: "0",
}}
></i>
</Button>
<Card.Body>{number}</Card.Body>
</Card>
);
};
export default MainCard;
Upvotes: 1
Reputation: 1309
You need to use useState().
For example.
import { useState } from "react";
export default function App() {
const [{items}, setItems] = useState({ items: [] });
const addItem = () => {
items.push(<div key={items.length}>Hello</div>);
setItems({ items: [...items] });
};
return (
<div>
<button onClick={addItem} />
{items}
</div>
);
}
This creates a persistant storage that you can set values into that remain between component rebuilds. The object in storage has a single key "items" with an array containing the items. It is convenient to put your state in an object with a key that describes the contents. This allows you to tell what state object you are looking at if you need to debug your app.
The addItem() callback will add a new item into the object in state. Importantly, the item has a key attribute that should be unique. This helps react monitor the state of elements in an array.
You can see a demo of the above code here
Upvotes: 2