Design comparison
Solution retrospective
I have a problem with the buttons, i cannot select just only one although i used useState and other methods. Once clicked every button is selected. Could you please help me ?
Community feedback
- @nicodes-devPosted over 1 year ago
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 helpful3@elic4vetPosted over 1 year ago@nicodes-dev Thank you for your help and your time :) Your answer helped me a lot !! :)
1 - @webguy83Posted over 1 year ago
Using React for this kind of simple widget is definitely way beyond overkill. A much more simple approach to this can be to make a simple HTML form with 5 radio buttons along with a submit button and using css to style them. The only JavaScript you'd need is an submit button handler to detect the value selected. This functionality is already done for you using radio buttons.:). No need to bring React in to complicate it.
0 - @FedeNicolettiPosted over 1 year ago
Hi! It looks like the issue is with the isActive state. Currently, you are using a single isActive state for all buttons, which means that when you click on any button, all buttons will be toggled to the same state. To fix this, you need to have a separate state for each button to keep track of its selected status. Maybe you can try something like this:
import React, { useState } from "react"; import icon from "../assets/images/icon-star.svg"; import img from ".././assets/images/illustration-thank-you.svg"; import "../App.css"; const Rating = () => { const [selectedNumber, setSelectedNumber] = useState(0); const [success, setSuccess] = useState(false); const handleClick = (numberSelected) => { setSelectedNumber(numberSelected); }; const handleSubmit = () => { if (selectedNumber === 0) { alert("Please select a rating!"); } else { setSuccess(true); } }; const ratingButtons = Array.from({ length: 5 }, (_, index) => index + 1); let successComponent = null; if (success) { successComponent = ( <div> <img src={img} alt="Success" /> <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> ); } return ( <div className="container"> <div className="card"> <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"> {ratingButtons.map((number) => ( <div className="circle" key={number}> <button className={selectedNumber === number ? "selected" : ""} onClick={() => handleClick(number)} value={number} > {number} </button> </div> ))} </div> </div> <button className="submit-btn" onClick={handleSubmit}> Submit </button> </div> <div className="attribution"> Challenge by{" "} <a href="https://www.frontendmentor.io?ref=challenge" target="_blank" rel="noreferrer" > Frontend Mentor </a>{" "} Coded by{" "} <a href="https://www.linkedin.com/in/eerkekoglou/">Elisabeth Erkekoglou</a> </div> </div> ); }; export default Rating;
0
Please log in to post a comment
Log in with GitHubJoin our Discord community
Join thousands of Frontend Mentor community members taking the challenges, sharing resources, helping each other, and chatting about all things front-end!
Join our Discord