Sprawa wygląda w ten sposób: kontrolowane elementy formularza używają callbacków do updateu sateów, Gdy chcę odczytać ich wartość z innego callbacku, zawsze widnieje w nich wartość początkowa/domyślna. Kilka commitów wcześniej działało i nie zauważyłem żadnych znaczących zmian przy kontrolowanych elementach :d
Mniej więcej tak to wygląda:
const [textAreaValue, setTextAreaValue] = useState<string>("")
const handleTextFormValueChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newValue = e.target.value //tu jest widoczna prawidłowa wartość podczas wywołania
setTextAreaValue(newValue)
}
const submitContactFormCallback = () => { // <----------------------------------- odczytywanie z kontrolowanego komponentu
if(contactFormStatus !== ContactFormStatus.IDLE)
return
console.log(`pre send area: ${textAreaValue}`) // <--------------------------- zawsze ma wartość domyślną
dispatch(submitContactForm(textAreaValue, titleValue))
}
<textarea
className="mb-10 py-4 px-2 bg-background-main font-content text-xl rounded-lg border-4 border-gray-400"
placeholder=""
onChange={handleTextFormValueChange}
value={textAreaValue}
rows={12}
/>
<button
disabled={!canSend}
onClick={submitContactFormCallback}
>
Send
</button>
Cały komponent:
export const Contact = () => {
const dispatch = useDispatch()
const contactFormStatus = useSelector(contactFormStatusSelector)
const contactFormResponse = useSelector(contactFormResponseSelector)
const [textAreaValue, setTextAreaValue] = useState<string>("")
const [titleValue, setTitleValue] = useState<string>("Hello")
const clearFormStateCallback = useCallback(() => {
dispatch(clearContactFormState())
}, [,])
const handleTitleValueChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newValue = e.target.value
setTitleValue(newValue)
}
const handleTextFormValueChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newValue = e.target.value
setTextAreaValue(newValue)
}
const submitContactFormCallback = () => {
if(contactFormStatus !== ContactFormStatus.IDLE)
return
console.log(`pre send area: ${textAreaValue}`) // <--------------------------- pusto tu
console.log(`pre send title: ${titleValue}`) // <--------------------------- zawsze ma wartosc domyslna "Hello"
dispatch(submitContactForm(textAreaValue, titleValue))
}
const ConnectedDialog = useMemo(() => {
const isError = contactFormStatus === ContactFormStatus.FAILED
//show when sending failed or succeeded
const showDialog = contactFormStatus === ContactFormStatus.FAILED || contactFormStatus === ContactFormStatus.SENT
return <Dialog
text={contactFormResponse}
className={BindClasses({
"bg-red-800 text-rawSalmon": isError,
"bg-deadSalmon text-black": !isError
}, " z-60 fixed flex flex-row flex-wrap items-center justify-between bottom-6 md:bottom-8 lg:bottom-12 left-1/2 m-1 w-11/12 lg:w-9/12 text-lg rounded-md shadow-2xl transform -translate-x-1/2")}
show={showDialog}
dialogButtons={[
//close button
{
callback: clearFormStateCallback,
// ! refactor, move outside :d
buttonContent: (
<>
{/* @ts-ignore "alt" here is fine */}
<svg alt="Close message" className="w-24 h-24" viewBox="0 0 24 24">
<path fill="currentColor" d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
</>),
className: "bg-rawSalmon rounded-md transition duration-200 text-purpleAcid hover:text-white hover:bg-purpleAcid"
}
]}
/>
}, [contactFormStatus, contactFormResponse]) //update dialog only when contactFormStatus or contactFormResponse changes state
const ConnectedSubmitButton = useMemo(() => {
const canSend = contactFormStatus === ContactFormStatus.IDLE
return <button
className={BindClasses({
"opacity-20 cursor-not-allowed hover:bg-purple-600 hover:no-underline": !canSend,
"hover:underline hover:bg-purple-700": canSend
}, "mx-4 mt-6 px-8 lg:px-12 py-4 w-auto h-auto rounded-2xl font-content self-center text-xl bg-purple-600 transition duration-200")}
disabled={!canSend}
onClick={submitContactFormCallback}
// ! for dev only
// onClick={e => { dispatch(dummySubmitContactFormWithSuccess(textAreaValue, titleValue)) }}
// onClick={e => { dispatch(dummySubmitContactFormWithError(textAreaValue, titleValue)) }}
>
Send
</button>
}, [contactFormStatus])
return (
// contact wrapper
<div id="contact" className="flex py-20 ml-2 lg:ml-12">
{/* dialog */}
{
ConnectedDialog
}
{/* contact form */}
<div className="flex flex-col m-auto w-10/12 lg:w-9/12 ">
<div className="font-display text-5xl">Contact form</div>
<select
className="my-6 px-4 py-2 h-16 bg-background-main font-content text-2xl rounded-lg border-4 border-gray-400 "
value={titleValue}
onChange={handleTitleValueChange}
>
<option value="first">Hello</option>
<option value="second">Other</option>
</select>
<textarea
className="mb-10 py-4 px-2 bg-background-main font-content text-xl rounded-lg border-4 border-gray-400"
name="textarea"
placeholder=""
onChange={handleTextFormValueChange}
value={textAreaValue}
rows={12}
/>
{
ConnectedSubmitButton
}
</div>
</div>
)
}