JavaScript cant get value of input from my items - javascript

making beginer site and im getting api of crypto Coins and i put them on home page . i put "buy" button with input that i want to fill and buy . i have trying to use getElementsByClassName/ id and its only return the first item. if i log the first item its return value . but if i log all the rest of the items its return "undefined". so i have search a hours on solution and i have found that im using the same "classname" to all items - because of that it return only the first item value . but the problem that i have no idea and i can't find solution.
adding my code . (the map is on homepage)
site image with log
coin.js
import React, { useContext, useState } from 'react'
import './Coin.css';
import { Context } from '../src/context/Context'
const Coin = ({
name,
price,
symbol,
marketcap,
volume,
image,
priceChange
}) => {
const { user, dispatch } = useContext(Context);
// buyCoin function ( buy coin -> checking if user can buy)
function buyCoin() {
const coinNumber = document.getElementsByClassName("buyInput").value;
const coinPrice = { price }.price;
const wallet = user.wallet;
if (coinNumber * coinPrice > wallet) {
alert("you dont have enough money");
console.log(coinNumber);
return false;
} else {
console.log(coinNumber);
alert("purchase completed");
return true;
};
}
return (
<div className='coin-container'>
<div className='coin-row'>
<div className='coin'>
<img src={image} alt='crypto' />
<h1>{name}</h1>
<p className='coin-symbol'>{symbol}</p>
</div>
<div className='coin-data'>
<p className='coin-price'>${price}</p>
<p className='coin-volume'>${volume.toLocaleString()}</p>
{priceChange < 0 ? (
<p className='coin-percent red'>{priceChange.toFixed(2)}%</p>
) : (
<p className='coin-percent green'>{priceChange.toFixed(2)}%</p>
)}
<p className='coin-marketcap'>
${marketcap.toLocaleString()}
</p>
//button to buy the coin
<button className='buybtn' onClick={buyCoin}>Buy</button>
<input type="number" className="buyInput"></input>
</div>
</div>
</div>
);
};
export default Coin;

If you want the value from the input field, I recommend using the state to keep track of the value.
Something like:
const { coinNumber, setCoinNumber } = useState();
And add
<input
type="number"
className="buyInput"
value={coinNumber}
onChange={setCoinNumber }
></input>
Then you can get your value from coinNumber to use in the buyCoin function.

Related

React: Managing dozens of button states in a function component

This is my first React project. I am using Bootstrap grid to make a "+" shaped cluster of buttons that go from 1 to 31, which means I cannot use map to reduce the amount of lines of code I have.
const ButtonPlusGrid = () => {
const [colorButtonID, setColorButtonID] = useState(0);
const handleColorChange = () => {
if (colorButtonID === 3) {
setColorButton1(1);
} else {
setColorButton1(colorButtonID + 1);
}
};
let buttonClass;
if (colorButtonID === 0) {
buttonClass = 'btn-secondary col-2';
// btn-secondary for gray color.
} else if (colorButtonID === 1) {
buttonClass = 'btn-success col-2';
// btn-succes for green.
} else if (colorButtonID === 2) {
buttonClass = 'btn-danger col-2';
// btn-danger for red.
} else if (colorButtonID === 3) {
buttonClass = 'btn-primary col-2';
// btn-primary for blue.
}
// the empty divs are to correct the spacing for each row.
return (
<>
<div className="row">
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
<button
type="button"
className={buttonClass}
id="num1"
onClick={handleColorChange}
>
1
</button>
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
</div>
<div className="row">
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
<button
type="button"
className="btn-secondary col-1"
id="num2"
>
2
</button>
<button
type="button"
className="btn-secondary col-1"
id="num3"
>
3
</button>
<div className="col-1" />
<div className="col-1" />
<div className="col-1" />
</div>
...
</>
);
};
I am trying to change the color of each button on click (by changing their class names) but doing it this way will be difficult, replicating the same logic 31 times is too much code, and I can't think of any elegant way to handle it.
I created the button grid using Bootstrap grid to make it follow a specific shape.
For each button, I assigned a specific ID (num1 => num31) because I am planning to save each state in a database and when I open the webpage again, each button keeps the same colors.
These are just 2 rows from the whole grid. I just wrote the code to change the color of one button based on the state.
My question is:
How do I handle all those 31 buttons? Is there a more efficient way to manage each state? or should I overhaul this code because it's repetitive?
I thought of creating a React class component but I don't have an idea how it can be designed for each button, but I'll let you guys decide what's the best approach here.
PS: here is how the buttons look like: Here is the link.
You can manage the state for all the buttons in 1 single state variable. I've created an example to demonstate
import React from "react";
// Just an example to demonstate the functionality, you can changes this to return the correct css class instead.
const getStyle = (number) => {
switch (number) {
case 3:
return "pink";
case 2:
return "green";
case 1:
return "purple";
default:
return "papaywhip";
}
};
export default function App() {
const [colorIdMapping, setColorIdMapping] = React.useState({});
const handleClick = (index) => {
setColorIdMapping({
...colorIdMapping, // Remember all the other values
[index]: ((colorIdMapping[index] || 0) + 1) % 4 // Set current index to number 0, 1, 2 or 3
});
};
return (
<div className="App">
{/* Demo of rendering 10 buttons, render them any way you like. */}
{Array.apply(0, Array(10)).map(function (x, i) {
return (
<div
style={{ background: getStyle(colorIdMapping[i]) }}
onClick={() => handleClick(i)}
>
button
</div>
);
})}
</div>
);
}
You could also create a button component that keeps track of its own state. But in this case I would manage all the state in one place like the example above, so you can easily store and retrieve it (from the db).
But as an example, it could look like this:
const MyButton = () => {
const [number, setNumber] = React.useState(0);
const handleClick = () => {
setNumber((number + 1) % 4);
};
return (
<div onClick={handleClick} style={{ background: getStyle(number) }}>
button
</div>
);
};
Working example: https://codesandbox.io/s/loving-feistel-jw8vci

React : Display an error message when clicking on the "submit" button if the user enters text

I create a project in React. I would like that when the user clicks on the "submit" button it shows him an error message if in the input "FormDebut" he enters "text" type characters.
But I can't do this :'( , could you help me please? Thanks in advance !
FormulaireReservations.js :
export default function FormulaireReservation() {
return (
<div className="Formulaire">
<Logo/>
<h2><center>Formulaire de réservation</center></h2>
<FormDebut/>
<center><input type="submit" value="Submit" /></center>
</div>
);
}
Component FormDebut.js :
const [value, setValue] = React.useState("");
const onChange = (e) => {
const formattedValue = formatInput(e.target.value);
if (formattedValue.length < 6) {
setValue(formattedValue);
}
}
const formatInput = (input) => {
if (input.length > 2 && input[2] !== ":") {
return input.substring(0, 2) + ":" + input.slice(2);
}
return input;
}
return (
<div className="Form">
<div>Début : </div>
<input placeholder="00:00" type="text" onChange={onChange} value={value}/>
</div>
)
}
export default FormDebut; ```
Test this out in a new document, and then from there you can implement it into your work. You can have a parameter were user has to input a number between 1-10 if they do not then you can display an error message. If number is between 1-10 then you can redirect them. isNAN (is not a number) is a condition and is used for validating the input of the user. The function will check if the input is not a number, it will check if the input is less than 1 or more than 10, if all these conditions are true then it will display an error.
<p>Please input a number between 1 and 10:</p>
<input id="numb">
<button type="button" onclick="myFunction()">Submit</button>
<p id="demo"></p>
<script>
function myFunction() {
// Get the value of the input field with id="numb"
let x = document.getElementById("numb").value;
// If x is Not a Number or less than one or greater than 10
let text;
if (isNaN(x) || x < 1 || x > 10) {
text = "Input not valid";
} else {
text = "Input OK";
}
document.getElementById("demo").innerHTML = text;
}
</script>
I'm not sure if I understood your requirements, but seems that when you click submit button you want to check if the input is numeric value. If that's the case, this should work:
https://codesandbox.io/s/infallible-browser-74r5d7?file=/src/App.js
I moved the value state into the Form itself and passing it as a prop to its child. I also added a new state variable called error that is passed to the FormDebut. On click of the submit button, I strip the : from the value and check if I can parse it to a number. If not, I set the error, otherwise I clear out the error.
Hope that helps
Here is something you can modify your components:
in your FormulaireReservations.js:
export default function FormulaireReservation() {
const [value, setValue] = React.useState("");
const [errorMsg, setErrorMsg] = React.useState("");
const handleSubmission = () => {
if (isNaN(value)) {
setErrorMsg('Input should not be a text')
}
}
React.useEffect(() => {
setErrorMsg('')
}, [value])
return (
<div className="Formulaire">
<Logo />
<h2><center>Formulaire de réservation</center></h2>
<FormDebut value={value} setValue={setValue} msg={errorMsg} />
<center><input type="button" onClick={handleSubmission} value="Submit" /></center>
</div>
);
}
and in your FormDebut.js :
function FormDebut({ value, setValue, msg }) {
const onChange = (e) => {
const formattedValue = formatInput(e.target.value);
if (formattedValue.length < 6) {
setValue(formattedValue);
}
}
const formatInput = (input) => {
if (input.length > 2 && input[2] !== ":") {
return input.substring(0, 2) + ":" + input.slice(2);
}
return input;
}
return (
<div className="Form">
<div>Début : </div>
<input placeholder="00:00" type="text" onChange={onChange} value= {value} />
{ msg && <p>{msg}</p> }
</div>
)}export default FormDebut;
So, basically, the value and errorMsg state is passed to another component that contains the input field. Each time changes on the input field will also update the state declared in the parent component. When a user clicks on submit button, it calls a function that is checking if the value is a number or not. Based on that the errorMsg state is updated. And in the FormDebut component, error msg will be shown just after the input field if any msg was set.

Increment and decrement counter according to select or unselect tag

I am working with the next js to handle a question form, I have multiple questions that have multiple answers, and the user may select one or multiple tags for a question.
if a user selects one or more tag to answer a question it must increment the counter just one time, If a user unselect a tag it must not decrement until unselect the last tag of the related question.
1 - When the user selects a tag of a question at first increment the counter, but it must not increment when I select the second tag in the same question.
2 - When the user unselects tags of question it must not decrement if any one of them is selected it must not decrement until the last one is unselected.
Now when selecting the first tag it increments the counter but after that, if I select or unselect, it does not decrement or increment.
Please help me to handle this.
My code:
<TageQuestion
key={item.attributes.Number}
id={item.attributes.Number}
data={item}
name={`${item.attributes.Number}`}
errors={errors}
touched={touched}
handleSelectOption={handleSelectOption}
/>
The component:
<div className="mt-3">
{Object.keys(attributes.options).map(item => (
<div className="inline" key={item}>
<input
{...feild}
{...props}
className="hidden"
id={item}
value={item}
type="checkbox"
name={`${name}[answer]`}
/>
<label
htmlFor={item}
id={item + "lable"}
onClick={e => {
selectTage(e.target);
handleSelectOption(`question${id}`, item);
}}
className="inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium bg-gray-100 text-gray-800 mr-3 mb-2 cursor-pointer"
>
{item}
</label>
</div>
))}
</div>
The function:
function handleSelectOption(title, item) {
setCompareTagTitle(title);
if (title !== compareTagTitle) {
setTagItem([...tagItem, item]);
setCounter(++counter);
} else if (title === compareTagTitle) {
setCounter(counter);
} else {
setCounter(--counter);
}
}
Data that come from the backend:
we should consider 2 steps first in child component TagQuestions then in parent component [type].js
step1:
//TagQuestions.js
const [intialRender, setIntialRender] = useState(true);
// if component render for first time
const selectTag = (e) => {
// we should change intialRender to false to say that user select somthing
....
if (tage.length > 0) {
const isExit = tage.filter((item) => item.name === el.innerHTML);
if (isExit.length === 0) {
setTage([...tage, { name: el.innerHTML }]);
if (intialRender) {
setIntialRender(false);
}
}
} else {
setTage([...tage, { name: el.innerHTML }]);
if (intialRender) {
setIntialRender(false);
}
}
....
}
//use effect function to if user unselect all tags after selctions
useEffect(() => {
if (tage.length === 0 && !intialRender) {
//call parent function and set empty param to true.
handleSelectOption(`question${id}`, "", true);
}
}, [tage]);
step2(parent component):
const [compareTagTitle, setCompareTagTitle] = useState([]);
function handleSelectOption(title, item, empty = false) {
if (empty) {
const filter = compareTagTitle.filter(
(value) => value.toString() !== title.toString()
)
setCompareTagTitle(filter);
setCounter(--counter);
} else {
if (!compareTagTitle.includes(title)) {
setCompareTagTitle([...compareTagTitle, title]);
setCounter(++counter);
}
}
}

how to disable a button if more than once check box is checked in React JS

I have to create a button that activates when a check box is checked and disables when unchecked.
I was able to achieve this by the following code.
import React from "react";
import "./styles.css";
import{useState} from 'react'
export default function App() {
const [change, setChange] = useState(true);
function buttonHandler(){
setChange(!change)
}
return (
<div className="App">
<button disabled={change}>Click Me</button>
<input type="checkbox" onChange={buttonHandler}/>
<input type="checkbox" onChange={buttonHandler}/>
<input type="checkbox" onChange={buttonHandler}/>
</div>
);
}
Now I have another challenge where I have to keep it disabled if more than 1 check box is checked. I tried to use object and array manipulation but it does not work. Any advice on how this can be achieved.
import React from "react";
import{useState} from 'react'
export default function App() {
const [checkboxStatus, setCheckboxStatus] = useState(Array(3).fill(false));
function buttonHandler(index){
let status = [...checkboxStatus];
status[index] = !status[index]
setCheckboxStatus(status)
}
return (
<div className="App">
<button disabled={checkboxStatus.filter(status => status === true).length != 1}>Click Me</button>
{Array(3).fill(0).map((_, index) => <input type="checkbox" checked={checkboxStatus[index]} onChange={() => buttonHandler(index)}/>)}
</div>
);
}
You can do that by keeping track of the status of the checkbox rather than tracking the status of the button. This is because if you know the status of all the checkboxes, you can easily calculate the status of the button.
I have also taken the liberty of converting the checkbox to map it since the content is the same. You can do the same by passing the index to each of them. Something like <input type="checkbox" onChange={() => buttonHandler(0}/> and so on for each of the inputs.
Wrap your input elements in a parent element like form or div.
Maintain state; an array that contains each box status (either true or false). When a box is changed call the handleChange which will update the state.
The button disabled property should call a function called isDisabled which will check to see if there are zero boxes checked or more than one box checked, returning true if the condition is matched.
const { useEffect, useState } = React;
function Example() {
const [boxes, setBoxes] = useState([]);
// In the text I suggested wrapping the inputs in
// a parent element. This was so we could use the following
// code to find its index within the list of inputs
// without adding any more code to the JSX
// Cribbed from https://stackoverflow.com/a/39395069/1377002
function handleChange(e) {
// Destructure the children from the parent of
// the element that was changed (ie all the input elements)
const { parentNode: { children } } = e.target;
// Find the index of the box that was changed
const index = [...children].indexOf(e.target);
// Copy the state
const newState = [...boxes];
// Toggle the boolean at the index of
// the `newState` array
newState[index] = !newState[index];
// Set the state with the updated array
setBoxes(newState);
}
// `filter` the boxes that return true.
// Return true if the length is 0 or > 1.
function isDisabled() {
const len = boxes.filter(box => box).length;
return len === 0 || len > 1;
}
return (
<div className="App">
<button disabled={isDisabled()}>Click Me</button>
<form>
<input type="checkbox" onChange={handleChange}/>
<input type="checkbox" onChange={handleChange}/>
<input type="checkbox" onChange={handleChange}/>
</form>
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation
Destructuring assignment
Spread syntax

How do you pass the value inputted from a prompt to a function?

I am creating a hangman game using react and js. After pressing the Guess word button I want to use a prompt box to have the user enter what they think the full word is. However, when I input a word into the prompt box I get "TypeError: Object doesn't support property or method 'checkWholeWord'". I am new to js coding and I really don't know what I am doing wrong here.
import React, { Component } from 'react';
import './App.css';
import words from './words'
class App extends Component {
//chooses random word
state = {
word: words[Math.floor(Math.random() * words.length)],
guessedLetters: [],
guessesRemaining: 6
}
//pass function to child to update
updateGuessedLetters(letter){
if(this.state.guessedLetters.includes(letter)){
alert('ALREADY TRIED THAT')
}else{
this.setState({
//makes new array with letter
guessedLetters: [...this.state.guessedLetters, letter]
})
}
}
//keeps track of how many times wrong letter is entered
updateGuessesRemaining(letter){
if(!this.state.guessedLetters.includes(letter) && !this.state.word.split('').includes(letter)){
this.setState({guessesRemaining: this.state.guessesRemaining -1})
}
}
//fix this function
wordIsGuessed(){
const guessState = this.state.word.split('').map(letter => {
if (this.state.guessedLetters.includes(letter)){
return letter
}
})
return guessState.join('') === this.state.word
}
//checks if game is over by checking guesses remaining
gameOver(){
if(this.state.guessesRemaining <= 1){
alert('You lose')
}else if (this.wordIsGuessed()){
alert('You win!')
}
}
//when someone attempts to guess whole word checks inputted word
checkWholeWord(wholeWord){
if (wholeWord===this.state.word){
alert('You Win')
}
}
//called every time a letter is submitted, if gameover then end
updateGameState(letter){
this.gameOver()
this.updateGuessedLetters(letter)
this.updateGuessesRemaining(letter)
}
render() {
return (
<div className="App">
<h1>Hangman</h1>
{this.state.word}
<DisplayWord word={this.state.word} guessedLetters={this.state.guessedLetters}/>
<SubmitGuess updateGameState = { (letter) => this.updateGameState(letter)}/>
<WrongLetters word={this.state.word} guessedLetters={this.state.guessedLetters}/>
</div>
);
}
}
class DisplayWord extends Component{
render(){
//splits word into array
const wordLetters = this.props.word.split('')
const answer = wordLetters.map(letter => {
let letterState = '_' + ' '
if(this.props.guessedLetters.includes(letter)){
letterState=letter
}
return letterState
})
return(
<div className="DisplayWord">
<h3>{answer}</h3>
</div>
);
}
}
class WrongLetters extends Component {
getWrongLetters(){
//filter checks if letter is in word
const wrong = this.props.guessedLetters.filter(letter =>{
return !this.props.word.split('').includes(letter)
})
return wrong
}
render(){
return(
<div className="WrongLetters">
{this.getWrongLetters()}
</div>
);
}
}
//takes guess from user
class SubmitGuess extends Component {
//ensures that the page doesn't refresh completely
handleSubmit = (e) => {
e.preventDefault()
//gets value from user
const input = this.refs.textInput.value
this.props.updateGameState(input)
//resets value to empty
this.refs.textInput.value = ''
}
guessWholeWord = (e) => {
e.preventDefault()
const wholeWord = prompt ("Guess the word :)")
this.props.checkWholeWord(wholeWord)
}
render(){
return(
<div>
<form ref="form" onKeyUp={this.handleSubmit}>
<input type="text" ref='textInput' maxLength='1'/>
<button>New Game</button>
</form>
<form ref="forms" onSubmit={this.guessWholeWord}>
<button type ="submit">Guess Word</button>
</form>
</div>
);
}
}
export default App;
guessWholeWord = (e) => {
e.preventDefault()
const wholeWord = prompt ("Guess the word :)")
if (wholeWord===this.state.word){
alert('You win!')
}
}
render() {
return (
<div className="App">
<h1>Hangman</h1>
{this.state.word}
<DisplayWord word = {this.state.word} guessedLetters={this.state.guessedLetters}/>
<SubmitGuess updateGameState = { (letter) => this.updateGameState(letter)}/>
<WrongLetters word={this.state.word} guessedLetters={this.state.guessedLetters}/>
<form ref="forms" onSubmit={this.guessWholeWord}>
<button type ="submit">Guess Word</button>
</form>
</div>
);

Categories

Resources