如何将按钮选中的预算值同步到React Final Form并提交
我最近在做一个多步骤表单项目,用了React Final Form和Material-UI,想要实现点击$300、$500、$1000这三个按钮,自动把对应数值填入campaignAmount输入框里。现在按钮点了输入框能显示对应值,但提交表单的时候这个值根本没被Final Form收录进去,折腾了好半天,终于找到问题所在了!
问题根源
你现在是用本地状态txtValue来更新输入框显示,但Final Form有自己的状态管理机制,直接改输入框或者本地状态,表单根本不知道这个值变了,自然提交的时候不会带上它。
解决步骤
核心就是要用Final Form提供的form.change()方法来同步更新表单的状态,而不是自己维护本地状态。
移除无用的本地状态
先把不需要的txtValue状态删掉,我们不需要它了:// 删掉这一行 // const [txtValue, setTxtValue] = useState({})修改按钮点击事件处理函数
利用表单render回调里拿到的form对象,调用form.change()来更新campaignAmount字段的值:function schemaTypeSelectionHandle(event, form) { const amount = event.target.value; // 用form.change同步更新表单状态,转成Number类型适配输入框的number类型 form.change('campaignAmount', Number(amount)); }调整按钮组的点击绑定
把点击事件直接绑定到每个按钮上,传递form对象给处理函数:<ButtonGroup color="primary" aria-label="outlined primary button group"> <Button value='300' onClick={(e) => schemaTypeSelectionHandle(e, form)}>$300</Button> <Button value='500' onClick={(e) => schemaTypeSelectionHandle(e, form)}>$500</Button> <Button value='1000' onClick={(e) => schemaTypeSelectionHandle(e, form)}>$1000</Button> </ButtonGroup>顺便修复一个小拼写错误
你的KeyboardDatePicker里dateFunsUtils拼写错了,应该是dateFnsUtils,不然日期选择器可能会出问题:<KeyboardDatePicker label="Start Date" format="dd-MM-yyyy" name="startDate" dateFnsUtils={DateFnsUtils} // 修正拼写 required={true} value={new Date()} /> <KeyboardDatePicker label="End Date" format="dd-MM-yyyy" name="endDate" dateFnsUtils={DateFnsUtils} // 同样修正这里 value={new Date(nextyear,nextmonth - 1,nextday)} required={true}/>同步修改外部检测组件的字段名
把ExternalModificationDetector的name改成campaignAmount,这样它才能正确检测到目标字段的外部修改:<ExternalModificationDetector name="campaignAmount"> {externallyModified => ( <BooleanDecay value={externallyModified} delay={1000}> {highlight => ( <TextField name="campaignAmount" id="campaignAmount" component="input" type="number" placeholder="Total" style={{ transition: 'background 500ms ease-in-out', background: highlight ? 'yellow' : 'none' }} /> )} </BooleanDecay> )} </ExternalModificationDetector>
修改后的完整核心代码
import React from 'react'; import Wizard from '../wrappers/Wizard' import { Form, Field } from 'react-final-form' import ExternalModificationDetector from '../wrappers/ExternalModificationDetector'; import BooleanDecay from '../wrappers/BooleanDecay' import { Typography, Paper, Grid, Button, ButtonGroup, Box, CssBaseline, } from '@material-ui/core'; import DateFnsUtils from '@date-io/date-fns'; import { TextField, Radios, KeyboardDatePicker, } from 'mui-rff'; const onSubmit = async values => { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); await sleep(300); window.alert(JSON.stringify(values, 0, 2)); }; // MAIN FORM AREA export default function BuildAdStepper(props: MyFormProps) { const today = new Date(); const dateTimeFormat = new Intl.DateTimeFormat('en', { year: 'numeric', month: '2-digit', day: '2-digit' }) const [{ value: month },,{ value: day },,{ value: year }] = dateTimeFormat.formatToParts(today) const nmonth = new Date(today.getFullYear(), today.getMonth()+1, day); const [{ value: nextmonth },,{ value: nextday },,{ value: nextyear }] = dateTimeFormat.formatToParts(nmonth) function schemaTypeSelectionHandle(event, form) { const amount = event.target.value; form.change('campaignAmount', Number(amount)); } const calculator = createDecorator({ field: /day\[\d\]/, // when a field matching this pattern changes... updates: { // ...update the total to the result of this function total: (ignoredValue, allValues) => (allValues.day || []).reduce((sum, value) => sum + Number(value || 0), 0) } }) return ( <div style={{ padding: 16, margin: 'auto', maxWidth: 600 }}> <CssBaseline /> <Typography variant="h5" align="center" component="h2" gutterBottom> Create your campaign </Typography> <Paper style={{ padding: 16 }}> <Form onSubmit={onSubmit} decorators={[calculator]} render={({ handleSubmit, form, submitting, pristine, values }) => ( <Wizard initialValues={{promoting:"business", startDate:`${day}-${month}-${year }`,endDate:`${nextday}-${nextmonth}-${nextyear }`}} onSubmit={onSubmit} > <Wizard.Page> <Typography variant="h5">What are you Advertising</Typography> <Box m={2}/> <Radios name="promoting" formControlProps={{ margin: 'none' }} radioGroupProps={{ row: true }} data={[ { label: 'Business', value: 'business' }, { label: 'Community Project', value: 'community' }, { label: 'Event', value: 'event' }, ]} /> <Box m={2}/> </Wizard.Page> <Wizard.Page> <Typography variant="h5">Name Your Campaign</Typography> <Box m={2}/> <TextField name="campaignName" margin="none" fullWidth variant="outlined" required={true} /> <Box m={2}/> </Wizard.Page> <Wizard.Page> <Typography variant="h5">Set your schedule and budget</Typography> <Box m={2} /> <KeyboardDatePicker label="Start Date" format="dd-MM-yyyy" name="startDate" dateFnsUtils={DateFnsUtils} required={true} value={new Date()} /> <Box m={2} /> <KeyboardDatePicker label="End Date" format="dd-MM-yyyy" name="endDate" dateFnsUtils={DateFnsUtils} value={new Date(nextyear,nextmonth - 1,nextday)} required={true}/> <Box m={2} /> <Typography variant="h5">Your budget will be evenly distributed between start and end dates.</Typography> <Grid container> <Grid item lg={6}> <ButtonGroup color="primary" aria-label="outlined primary button group"> <Button value='300' onClick={(e) => schemaTypeSelectionHandle(e, form)}>$300</Button> <Button value='500' onClick={(e) => schemaTypeSelectionHandle(e, form)}>$500</Button> <Button value='1000' onClick={(e) => schemaTypeSelectionHandle(e, form)}>$1000</Button> </ButtonGroup> </Grid> <Grid item lg={3}> <ExternalModificationDetector name="campaignAmount"> {externallyModified => ( <BooleanDecay value={externallyModified} delay={1000}> {highlight => ( <TextField name="campaignAmount" id="campaignAmount" component="input" type="number" placeholder="Total" style={{ transition: 'background 500ms ease-in-out', background: highlight ? 'yellow' : 'none' }} /> )} </BooleanDecay> )} </ExternalModificationDetector> </Grid> </Grid> </Wizard.Page> </Wizard> )} /> </Paper> </div> ); }
为什么这样能行?
form.change()是Final Form提供的官方方法,专门用来手动更新表单字段的值,调用它之后,表单的状态会同步更新,输入框也会自动显示最新的值,而且提交的时候这个值就会被包含在表单数据里了。同时修正后的ExternalModificationDetector也能正确检测到字段的外部修改,高亮提示效果也能正常工作。
内容的提问来源于stack exchange,提问作者RussellHarrower

