I have a blog website that I'm working on in React. I have a form that allows users to make a blog post, and part of that form is a little slider we'll call a "fireLevelSlider" that allows users to select a fire level 1-5 based on how excited they are about the post. I have some functions that generate dynamic classNames to change the icons color/size when users hover/click on them. I am very new to react and I feel like the way I built this out is not efficient or within best practices, and I'm just curious how I should reformat this code, if at all.
It works now, but I just feel like the way I hardcoded the 5 elements into the blogPostForm just doesn't feel right. I was thinking maybe I should generate an array of img objects above the blogPostForm component function, and then call that array in the jsx? or Maybe I should be building my slider as its own component and importing it into the form? The only problem I have with that second option is that I'm setting state with the slider, and that state is held by the blogPostForm.
Here is my react code
import React from 'react'
import fireIcon from '../images/fireIcon.png'
export default function BlogPostForm () {
const [formState, setFormState] = React.useState({ flaire: '', title: '', text: ''});
const [isHovered, setIsHovered] = React.useState();
const [isLit, setIsLit] = React.useState();
function changeFlaire(event) {
const selectedFlaire = event.target.value;
setFormState( {...formState, flaire: selectedFlaire });
}
function changeTitle(event) {
const title = event.target.value;
setFormState( {...formState, title: title });
}
function changeText(event) {
const text = event.target.value;
setFormState( {...formState, text: text });
}
function handleMouseOver(e) {
setIsHovered(e.target.id);
}
function handleMouseLeave(e) {
setIsHovered();
}
function handleFireIconClick(e) {
setIsLit(e.target.id);
}
function handleFireIconClass(fireLevel) {
const classNames = ['fireIcon']
if (isHovered >= fireLevel) {
classNames.push('isHeld')
}
if (isLit >= fireLevel) {
classNames.push('isLit')
}
console.log(classNames)
return classNames.join(' ');
}
return (
<form className="postForm">
<h1 className="postFormHeader">Create a blog post!</h1>
<div className="fireIconContainer">
<img onClick={handleFireIconClick} onMouseLeave={handleMouseLeave} onMouseOver={handleMouseOver} className={handleFireIconClass(1)} src={fireIcon} alt="fireIcon" id="1"></img>
<img onClick={handleFireIconClick} onMouseLeave={handleMouseLeave} onMouseOver={handleMouseOver} className={handleFireIconClass(2)} src={fireIcon} alt="fireIcon" id="2"></img>
<img onClick={handleFireIconClick} onMouseLeave={handleMouseLeave} onMouseOver={handleMouseOver} className={handleFireIconClass(3)} src={fireIcon} alt="fireIcon" id="3"></img>
<img onClick={handleFireIconClick} onMouseLeave={handleMouseLeave} onMouseOver={handleMouseOver} className={handleFireIconClass(4)} src={fireIcon} alt="fireIcon" id="4"></img>
<img onClick={handleFireIconClick} onMouseLeave={handleMouseLeave} onMouseOver={handleMouseOver} className={handleFireIconClass(5)} src={fireIcon} alt="fireIcon" id="5"></img>
</div>
<select
required
className="flaireSelect"
value={formState.flaire}
onChange={changeFlaire}>
<option disabled={true} value="">Choose a flaire</option>
<option value="JavaScript">JavaScript</option>
<option value="CSS">CSS</option>
<option value="HTML">HTML</option>
<option value="REACT">REACT</option>
<option value="BACKEND">BACKEND</option>
</select>
<input
value={formState.title}
onChange={changeTitle}
className="titleBox"
placeholder="title"
type="text"
id="title"
name="title"
/>
<textarea
value={formState.text}
onChange={changeText}
className="textBox"
placeholder="text"
type="text"
id="blogPost"
name="blogPost"
/>
<div className="blogPostFormButtonContainer">
<button className="blogPostSubmit" type="submit">SUBMIT</button>
<button className="blogPostCancel" type="submit">CANCEL</button>
</div>
</form>
)
}
Here is my Css code
/*blogPostForm */
.postForm {
font-family: "Montserrat", sans-serif;
color: white;
background-color: var(--color2);
border-radius: 10px;
margin-top: 10px;
width: 80%;
max-width: 750px;
display: grid;
align-items: center;
justify-content: center;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-template-areas:
"flaire"
"title"
"post"
"buttons";
grid-gap: 5px;
}
.postFormHeader {
justify-self: center;
}
.fireIconContainer {
align-self: center;
}
.fireIcon {
color: white;
background-color: white;
width: 40px;
height: 40px;
transition: all 0.2s ease-in-out;
}
.isLit {
background-color: red;
}
.isHeld {
transform: scale(1.1);
}
.flaireSelect {
font-family: "Montserrat", sans-serif;
justify-self: center;
width: 150px;
height: 35px;
text-align: center;
}
select:invalid {
color: grey;
}
.titleBox {
font-family: "Montserrat", sans-serif;
height: 40px;
margin: 10px;
}
.textBox {
font-family: "Montserrat", sans-serif;
height: 100px;
margin: 10px;
resize: vertical;
}
.blogPostFormButtonContainer {
font-family: "Montserrat", sans-serif;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 50px;
margin-bottom: 10px;
}
.blogPostSubmit {
font-family: "Montserrat", sans-serif;
width: 35%;
margin: 5px;
height: 35px;
}
.blogPostCancel {
font-family: "Montserrat", sans-serif;
width: 35%;
margin: 5px;
height: 35px;
}