@nicodes-dev
Posted
Hi Elisabeth! I'm non-native english speaker so please bear with me. I review your code and it seems like you are a beginner in React but I assume you know "props" and "state". First let's identify what is the problem.
"I cannot select just only one although i used useState and other methods. Once clicked every button is selected."
The buttons are working fine, the problem is that the class "selected" applies to all buttons because of the conditional statement in the className.
className={isActive ? "selected" : ""}
In your code, isActive is a boolean which you set to true if "any" button is clicked, so all the buttons have the class 'selected' applied to it.
To fix this, use the selectedNumber state instead.
className={selectedNumber === '1' ? 'selected' : ''} //the class will only apply to the button which holds the value 1
You are also passing the whole event object in setSelectedNumber and you only use extract the value in the <p> tag.
const handleClick = (numberSelected) => {
setSelectedNumber(numberSelected)
}
<p className="result">You selected {selectedNumber.target.value} out of 5</p>
To fix this, you should extract the value and pass it in the setSelectedNumber.
const handleClick = (event) => {
const value = event.target.value
setSelectedNumber(value )
}
<p className="result">You selected {selectedNumber} out of 5</p>
The snippets above should be able to fix the problem.
Now to help you clean your code, I have written the snippets below.
This might be too long so you can just paste it in your code and then review it.
In your Rating component, you initialize the selectedNumber as a number which value is 0.
const [selectedNumber, setSelectedNumber] = useState(0);
When you get the value from the event object, any value will be converted to a string. So you can just initialize it with an empty string.
const [selectedNumber, setSelectedNumber] = useState("");
Then you can remove the isActive state because we will be using the selectedNumber to add the "selected" class. You also have to change the handleSubmit function. Your code should look like this
const [selectedNumber, setSelectedNumber] = useState(")
const [success, setSuccess] = useState(false)
const handleClick = event => {
setSelectedNumber(event.target.value)
}
const handleSubmit = () => {
// you can just check the length we initialize selectedNumber as a string.
if (selectedNumber.length === 0) {
alert('Please select a rating!')
} else {
setSuccess(true)
}
}
You can also remove this line of codes since this is not the proper way to write inline styles in react.
let rating_app = document.querySelector('.rating_app')
let submit_btn = document.querySelector('.submit-btn')
rating_app.style.display = 'none'
submit_btn.style.display = 'none'
You can directly apply it to the element without having to use a querySelector.
let successComponent = null
if (success) {
successComponent = (
<div>
<img src={img}/>
<p className="result">You selected {selectedNumber} out of 5</p>
<h1>Thank you !</h1>
<p>We appreciate you taking the time to give a rating. If you ever need
more support, don't hesitate to get in touch!</p>
</div>
)
}
Then in the return statement, we will use a ternary operator to render either successComponent or the rating div. Note: I remove the key and id attribute in your button because it is not needed in this solution.
return (
<div className="container">
<div className="card">
{success ? (
<div className="successComponent">{successComponent}</div>
) : (
<>
<div className="rating_app">
<img src={icon} className="icon" alt="icon" />
<h1>How did we do ? </h1>
<p>Please let us know how we did with your support request.
All feedback is appreciated to help us improve our offering!</p>
<div className="ratings">
<div className="circle">
<button
className={setSelectedNumber === '1' ? 'selected' : ''}
onClick={handleClick}
value="1"
>
1
</button>
</div>
<div className="circle">
<button
className={setSelectedNumber === '2' ? 'selected' : ''}
onClick={handleClick}
value="2"
>
2
</button>
</div>
<div className="circle">
<button
className={setSelectedNumber === '3' ? 'selected' : ''}
onClick={handleClick}
value="3"
>
3
</button>
</div>
<div className="circle">
<button
className={setSelectedNumber === '4' ? 'selected' : ''}
onClick={handleClick}
value="4"
>
4
</button>
</div>
<div className="circle">
<button
className={setSelectedNumber === '5' ? 'selected' : ''}
onClick={handleClick}
value="5"
>
5
</button>
</div>
</div>
</div>
<button className="submit-btn" onClick={handleSubmit}>
Submit
</button>
</>
)}
</div>
You can also store the values that you are going to use in the buttons in an array and then map it's value.
const btnArrays = ['1', '2', '3', '4', '5'].map((value, index) => {
return (
<div className="circle" key={index}>
<button
className={selectedNumber === value ? 'selected' : ''}
onClick={handleClick}
value={value}
>
{value}
</button>
</div>
)
})
Then use it to render the buttons.
return (
<div className="container">
<div className="card">
{success ? (
<div className="successComponent">{successComponent}</div>
) : (
<>
<div className="rating_app">
<img src={icon} className="icon" alt="icon" />
<h1>How did we do ? </h1>
<p>
Please let us know how we did with your support request. All
feedback is appreciated to help us improve our offering!
</p>
<p className="results"></p>
<div className="ratings">{btnArrays}</div>
</div>
<button className="submit-btn" onClick={handleSubmit}>
Submit{' '}
</button>
</>
)}
</div>
)
You can also separate the successComponent and btnsArray into another component. Then pass down the values you wish to pass to the component as props, remember that the Rating component holds the states.
ButtonComponents.jsx
const btnArrays = ['1', '2', '3', '4', '5']
const ButtonComponent = ({ selectedNumber, handleClick }) => {
return btnArrays.map((value, index) => {
return (
<div className="circle" key={index}>
<button
className={selectedNumber === value ? 'selected' : ''}
onClick={handleClick}
value={value}
>
{value}
</button>
</div>
)
})
}
export default ButtonComponent
SuccessComponent.jsx
const SuccessComponent = ({ img, selectedNumber }) => {
return (
<div className="successComponent">
<div>
<img src={img}></img>
<p className="result">You selected {selectedNumber} out of 5</p>
<h1>Thank you !</h1>
<p>
We appreciate you taking the time to give a rating. If you ever need
more support, don't hesitate to get in touch!
</p>
</div>
</div>
)
}
export default SuccessComponent
Then your Rating component should look like this.
Rating.jsx
import { useState } from 'react'
import icon from '../assets/images/icon-star.svg'
import img from '.././assets/images/illustration-thank-you.svg'
import '../App.css'
import ButtonComponent from './ButtonComponent'
import SuccessComponent from './SucessComponent'
const Rating = () => {
const [selectedNumber, setSelectedNumber] = useState('')
const [success, setSuccess] = useState(false)
const handleClick = event => {
setSelectedNumber(event.target.value)
}
const handleSubmit = () => {
if (selectedNumber.length === 0) {
alert('Please select a rating!')
} else {
setSuccess(true)
}
}
return (
<div className="container">
<div className="card">
{success ? (
<SuccessComponent img={img} selectedNumber={selectedNumber} />
) : (
<>
<div className="rating_app">
<img src={icon} className="icon" alt="icon" />
<h1>How did we do ? </h1>
<p>
Please let us know how we did with your support request. All
feedback is appreciated to help us improve our offering!
</p>
<p className="results"></p>
<div className="ratings">
{
<ButtonComponent
handleClick={handleClick}
selectedNumber={selectedNumber}
/>
}
</div>
</div>
<button className="submit-btn" onClick={handleSubmit}>
Submit{' '}
</button>
</>
)}
</div>
)
}
export default Rating
and finally, your App.jsx should not have any state, since your declare those in the Rating component.
import { React, useState } from 'react'
import Rating from './components/Rating.jsx'
import './App.css'
function App() {
//const [success, setSuccess] = useState(true);
//const [selectedNumber, setSelectedNumber] = useState(0);
return (
<>
<Rating />
</>
)
}
export default App
You can copy and paste all those snippets since I tested it first.
Marked as helpful
@elic4vet
Posted
@nicodes-dev Thank you for your help and your time :) Your answer helped me a lot !! :)
@nicodes-dev
Posted
@elic4vet Thank you 😀