I have a React component that takes as input some text through a textbox and a value selected out of eight radio buttons. I am using useState to manage value changes real-time. However, no matter which option I select, the value returned always remains the first one, "ama".
My component looks like this:
"use client"
import { useState, useRef } from "react"
import { Copy } from "lucide-react"
import { Button } from "./ui/button"
import { Textarea } from "./ui/textarea"
import toTitleCase from "../lib/toTitleCase"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
function TitleCaseInput() {
const [value, setValue] = useState("")
const [selectedStyle, setSelectedStyle] = useState("ama")
const textareaRef = useRef<HTMLTextAreaElement>(null) // Create a ref for the Textarea
const handleTextareaHover = () => {
if (textareaRef.current && !textareaRef.current.disabled) {
textareaRef.current.focus() // Focus the Textarea on hover
}
}
const handleTextareaChange = (
event: React.ChangeEvent<HTMLTextAreaElement>,
) => {
const input = event.target.value
const output = toTitleCase(input, selectedStyle)
setValue(output)
}
const handleCopyClick = () => {
navigator.clipboard.writeText(value)
}
const styles = [
"AMA",
"AP",
"APA",
"Bluebook",
"Chicago",
"MLA",
"NYT",
"Wikipedia",
]
const styleTips = [
"American Medical Association Manual of Style",
"The Associated Press Stylebook",
"Publication Manual of the American Psychological Association",
"The Bluebook: A Uniform System of Citation",
"The Chicago Manual of Style",
"The Modern Language Association Style Manual",
"The New York Times Manual of Style and Usage",
"Wikipedia: Manual of Style",
]
// console.log(selectedStyle)
return (
<div className="mx-2 flex flex-row gap-3 md:mx-4 md:last:mb-6 lg:mx-auto lg:max-w-full xl:max-w-8xl">
<div className="relative flex h-full flex-1 items-stretch md:flex-col">
<div className="relative flex flex-col w-full flex-grow p-4">
<div className="relative">
<Textarea
ref={textareaRef}
value={value}
onChange={handleTextareaChange}
onMouseEnter={handleTextareaHover}
rows={1}
maxRows={10}
autoFocus
placeholder="Your unformatted title..."
className="resize-none pr-12 text-base py-3 scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch"
/>
<Button
className="absolute bottom-1.5 right-[8px]"
variant="ghost"
aria-label="convert"
onClick={handleCopyClick}
>
<Copy
className={`text-green-600 ${value ? "" : "text-gray-400"}`}
/>
</Button>
</div>
<h2 className="mb-3 mt-6 font-bold text-2xl">Style Guide</h2>
<div>
<TooltipProvider>
<RadioGroup
defaultValue="ama"
value={selectedStyle}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setSelectedStyle(event.target.value)
}
className="grid xs:grid-cols-2 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-8 gap-4"
>
{styles.map((style, index) => (
<div key={style} className="col-span-1 space-x-2">
<Tooltip>
<RadioGroupItem
value={style.toLowerCase()}
id={`style${index + 1}`}
/>
<TooltipTrigger asChild>
<Label htmlFor={`style${index + 1}`}>{style}</Label>
</TooltipTrigger>
<TooltipContent className="bg-gray-600 text-white">
<p>{styleTips[index]}</p>
</TooltipContent>
</Tooltip>
</div>
))}
</RadioGroup>
</TooltipProvider>
</div>
</div>
</div>
</div>
)
}
export default TitleCaseInput
I even tried tracking the change with a console.log()
right before return()
, but it always prints "ama"! Logic tells me, the value of selectedStyle
should be "ama" upon page load. Then, every time a new radio option is selected, it should trigger setSelectedStyle()
and update selectedStyle
with the new value. But that somehow doesn’t seem to be happening. Where am I going wrong!?
When I changed value={selectedStyle}
to defaultValue={selectedStyle}
, the radio button clicked did start getting highlighted, but the value of selectedStyle
still remained unchanged, i.e. "ama". I tracked this by placing a console.log(selectedStyle)
right above return()
.
2
Answers
Try carrying this line of code
const [selectedStyle, setSelectedStyle] = useState("ama")
outside the function
function TitleCaseInput()
Cause it seems every time this particular function executes the
selectedStyle
variable is assigned back the initial valueama
.So take it outside the function to avoid this.
You have used the onChange function in RadioGroup components, to fix your problem you can either use onClick on the item:
or if you want to use RadioGroup you have to check which of its children is selected.
You can check here for similar examples.