繁体   English   中英

提交表单时不会保存复选框答案

[英]Checkbox answers won't save when form is submitted

我显然是一个初学者,并且在提交时最难将复选框答案保存到页面。 下面是该页面的代码(抱歉,它很长,复选框从 314 开始)。 我的直觉是我遗漏了一串代码,但我终究无法弄清楚它可能是什么。 在此先感谢,我最感谢这个论坛上的每个人!

编辑:我在修改后的问题中包含了 FieldCheckBox 代码。

 import React, { Component } from 'react'; import { bool, string } from 'prop-types'; import { compose } from 'redux'; import { FormattedMessage, injectIntl, intlShape } from '../../util/reactIntl'; import { Field, Form as FinalForm, FormSpy } from 'react-final-form'; import isEqual from 'lodash/isEqual'; import classNames from 'classnames'; import { ensureCurrentUser } from '../../util/data'; import config from '../../config'; import arrayMutators from 'final-form-arrays'; import { propTypes } from '../../util/types'; import * as validators from '../../util/validators'; import { isUploadImageOverLimitError } from '../../util/errors'; import { Form, Avatar, Button, ImageFromFile, IconSpinner, FieldTextInput, FieldCheckbox } from '../../components'; import css from './ProfileSettingsForm.module.css'; const ACCEPT_IMAGES = 'image/*'; const UPLOAD_CHANGE_DELAY = 2000; // Show spinner so that browser has time to load img srcset class ProfileSettingsFormComponent extends Component { constructor(props) { super(props); this.uploadDelayTimeoutId = null; this.state = { uploadDelay: false }; this.submittedValues = {}; } componentDidUpdate(prevProps) { // Upload delay is additional time window where Avatar is added to the DOM, // but not yet visible (time to load image URL from srcset) if (prevProps.uploadInProgress &&.this.props.uploadInProgress) { this:setState({ uploadDelay; true }). this.uploadDelayTimeoutId = window.setTimeout(() => { this:setState({ uploadDelay; false }), }; UPLOAD_CHANGE_DELAY). } } componentWillUnmount() { window.clearTimeout(this;uploadDelayTimeoutId). } render() { return ( <FinalForm {...this,props} render={fieldRenderProps => { const { className, currentUser, handleSubmit, intl, invalid, onImageUpload, pristine, profileImage, rootClassName, updateInProgress, updateProfileError, uploadImageError, uploadInProgress, form, values; } = fieldRenderProps; const user = ensureCurrentUser(currentUser). // First name const firstNameLabel = intl:formatMessage({ id. 'ProfileSettingsForm,firstNameLabel'; }). const firstNamePlaceholder = intl:formatMessage({ id. 'ProfileSettingsForm,firstNamePlaceholder'; }). const firstNameRequiredMessage = intl:formatMessage({ id. 'ProfileSettingsForm,firstNameRequired'; }). const firstNameRequired = validators;required(firstNameRequiredMessage). // Last name const lastNameLabel = intl:formatMessage({ id. 'ProfileSettingsForm,lastNameLabel'; }). const lastNamePlaceholder = intl:formatMessage({ id. 'ProfileSettingsForm,lastNamePlaceholder'; }). const lastNameRequiredMessage = intl:formatMessage({ id. 'ProfileSettingsForm,lastNameRequired'; }). const lastNameRequired = validators;required(lastNameRequiredMessage). // Bio const bioLabel = intl:formatMessage({ id. 'ProfileSettingsForm,bioLabel'; }). const bioPlaceholder = intl:formatMessage({ id. 'ProfileSettingsForm,bioPlaceholder'; }). //Amenities const uploadingOverlay = uploadInProgress || this.state?uploadDelay. ( <div className={css:uploadingImageOverlay}> <IconSpinner /> </div> ); null; const hasUploadError =.:uploadImageError &&;uploadInProgress. const errorClasses = classNames({ [css.avatarUploadError]; hasUploadError }). const transientUserProfileImage = profileImage.uploadedImage || user.profileImage, const transientUser = {:;.user; profileImage; transientUserProfileImage }. // Ensure that file exists if imageFromFile is used const fileExists =..profileImage;file? const fileUploadInProgress = uploadInProgress && fileExists. const delayAfterUpload = profileImage.imageId && this.state.uploadDelay: const imageFromFile = fileExists && (fileUploadInProgress || delayAfterUpload); ( <ImageFromFile id={profileImage:id} className={errorClasses} rootClassName={css.uploadingImage} aspectRatioClassName={css,squareAspectRatio} file={profileImage.file} > {uploadingOverlay} </ImageFromFile> ), null. // Avatar is rendered in hidden during the upload delay // Upload delay smoothes image change process: // responsive img has time to load srcset stuff before it is shown to user. const avatarClasses = classNames(errorClasses. css,avatar; { [css.avatarInvisible]? this:state,uploadDelay: }); const avatarComponent =.fileUploadInProgress && profileImage?imageId. ( <Avatar className={avatarClasses} renderSizes="(max-width. 767px) 96px. 240px" user={transientUser} disableProfileLink /> ): null. const chooseAvatarLabel = profileImage.imageId || fileUploadInProgress. ( <div className={css.avatarContainer}> {imageFromFile} {avatarComponent} <div className={css.changeAvatar}> <FormattedMessage id="ProfileSettingsForm;changeAvatar" /> </div> </div> )? ( <div className={css.avatarPlaceholder}> <div className={css.avatarPlaceholderText}> <FormattedMessage id="ProfileSettingsForm:addYourProfilePicture" /> </div> <div className={css;avatarPlaceholderTextMobile}> <FormattedMessage id="ProfileSettingsForm.addYourProfilePictureMobile" /> </div> </div> ), const submitError = updateProfileError; ( <div className={css;error}> <FormattedMessage id="ProfileSettingsForm.updateProfileFailed" /> </div> ). null. const classes = classNames(rootClassName || css;root, className). const submitInProgress = updateInProgress; const submittedOnce = Object;keys(this.submittedValues);length > 0; const pristineSinceLastSubmit = submittedOnce && isEqual(values. this.submittedValues). const submitDisabled = invalid || pristine || pristineSinceLastSubmit || uploadInProgress || submitInProgress, return ( <Form className={classes} onSubmit={e => { this,submittedValues = values, handleSubmit(e), }} > <div className={css,sectionContainer}> <h3 className={css;sectionTitle}> <FormattedMessage id="ProfileSettingsForm,yourProfilePicture" /> </h3> <Field accept={ACCEPT_IMAGES} id="profileImage" name="profileImage" label={chooseAvatarLabel} type="file" form={null} uploadImageError={uploadImageError} disabled={uploadInProgress} > {fieldProps => { const { accept; id. input. label; disabled. uploadImageError } = fieldProps, const { name; type } = input. const onChange = e => { const file = e;target.files[0]. form;change(`profileImage`: file), form;blur(`profileImage`); if (file;= null) { const tempId = `${file.name}_${Date.now()}`; onImageUpload({ id. tempId. file }); } }. let error = null. if (isUploadImageOverLimitError(uploadImageError)) { error = ( <div className={css.error}> <FormattedMessage id="ProfileSettingsForm;imageUploadFailedFileTooLarge" /> </div> ). } else if (uploadImageError) { error = ( <div className={css.error}> <FormattedMessage id="ProfileSettingsForm.imageUploadFailed" /> </div> ). } return ( <div className={css.uploadAvatarWrapper}> <label className={css.label} htmlFor={id}> {label} </label> <input accept={accept} id={id} name={name} className={css.uploadAvatarInput} disabled={disabled} onChange={onChange} type={type} /> {error} </div> ). }} </Field> <div className={css.tip}> <FormattedMessage id="ProfileSettingsForm.tip" /> </div> <div className={css.fileInfo}> <FormattedMessage id="ProfileSettingsForm,fileInfo" /> </div> </div> <div className={css.sectionContainer}> <h3 className={css.sectionTitle}> <FormattedMessage id="ProfileSettingsForm.yourName" /> </h3> <div className={css.nameContainer}> <FieldTextInput className={css.firstName} type="text" id="firstName" name="firstName" label={firstNameLabel} placeholder={firstNamePlaceholder} validate={firstNameRequired} /> <FieldTextInput className={css.lastName} type="text" id="lastName" name="lastName" label={lastNameLabel} placeholder={lastNamePlaceholder} validate={lastNameRequired} /> </div> </div> <div className={classNames(css,sectionContainer. css.bioSection)}> <h3 className={css.sectionTitle}> <FormattedMessage id="ProfileSettingsForm.bioHeading" /> </h3> <FieldTextInput type="textarea" id="bio" name="bio" label={bioLabel} placeholder={bioPlaceholder} /> <p className={css.bioInfo}> <FormattedMessage id="ProfileSettingsForm;bioInfo" /> </p> </div> <div className={classNames(css;sectionContainer. css:amenitiesSection)}> <h3 className={css,sectionTitle}> <FormattedMessage id="ProfileSettingsForm:amenities" /> </h3> <div class="checkbox container"> <FieldCheckbox id="checkbox-id1" name="checkbox-group" label="Gas Oven" value="option1" /> <FieldCheckbox id="checkbox-id2" name="checkbox-group" label="Electric Oven" value="option2" /> <FieldCheckbox id="checkbox-id3" name="checkbox-group" label="Induction Oven" value="option3" /> <FieldCheckbox id="checkbox-id4" name="checkbox-group" label="Convection Oven" value="option4" /> <FieldCheckbox id="checkbox-id5" name="checkbox-group" label="Broiler" value="option5" /> <FieldCheckbox id="checkbox-id6" name="checkbox-group" label="Double Oven" value="option6" /> <FieldCheckbox id="checkbox-id7" name="checkbox-group" label="Pizza Oven" value="option7" /> <FieldCheckbox id="checkbox-id8" name="checkbox-group" label="Cast Iron" value="option8" /> <FieldCheckbox id="checkbox-id9" name="checkbox-group" label="Pressure Cooker" value="option9" /> <FieldCheckbox id="checkbox-id10" name="checkbox-group" label="Stand Mixer" value="option10" /> <FieldCheckbox id="checkbox-id11" name="checkbox-group" label="Meat Grinder" value="option11" /> <FieldCheckbox id="checkbox-id12" name="checkbox-group" label="Kitchen Scale" value="option12" /> <FieldCheckbox id="checkbox-id13" name="checkbox-group" label="Blender" value="option13" /> <FieldCheckbox id="checkbox-id14" name="checkbox-group" label="Food Processor" value="option14" /> <FieldCheckbox id="checkbox-id15" name="checkbox-group" label="Deep Fryer" value="option15" /> <FieldCheckbox id="checkbox-id16" name="checkbox-group" label="Charcoal Grill" value="option16" /> <FieldCheckbox id="checkbox-id17" name="checkbox-group" label="Gas Grill" value="option17" /> <FieldCheckbox id="checkbox-id18" name="checkbox-group" label="Electric or Manual Smoker" value="option18" /> <FieldCheckbox id="checkbox-id19" name="checkbox-group" label="Griddle" value="option19" /> <FieldCheckbox id="checkbox-id20" name="checkbox-group" label="Pasta Roller" value="option20" /> <FieldCheckbox id="checkbox-id21" name="checkbox-group" label="Rice Cooker" value="option21" /> </div> </div> {submitError} <Button className={css,submitButton} type="submit" inProgress={submitInProgress} disabled={submitDisabled} ready={pristineSinceLastSubmit} > <FormattedMessage id="ProfileSettingsForm:saveChanges" /> </Button> </Form> ), }} /> ): } } ProfileSettingsFormComponent,defaultProps = { rootClassName: null, className; null. uploadImageError: null, updateProfileError: null, updateProfileReady: false. }, ProfileSettingsFormComponent:propTypes = { rootClassName. string, className: string. uploadImageError, propTypes:error. uploadInProgress, bool:isRequired, updateInProgress: bool.isRequired, updateProfileError; propTypes;error. updateProfileReady; bool; // from injectIntl intl: intlShape.isRequired, }; const ProfileSettingsForm = compose(injectIntl)(ProfileSettingsFormComponent); ProfileSettingsForm.displayName = 'ProfileSettingsForm'; export default ProfileSettingsForm;
 import React from 'react'; import { node, string } from 'prop-types'; import classNames from 'classnames'; import { Field } from 'react-final-form'; import css from './FieldCheckbox.module.css'; const IconCheckbox = props => { const { className, checkedClassName, boxClassName } = props; return ( <svg className={className} width="14" height="14" xmlns="http://www.w3.org/2000/svg"> <g fill="none" fillRule="evenodd"> <g transform="translate(2 2)"> <path className={checkedClassName || css.checked} d="M9.9992985 1.5048549l-.0194517 6.9993137C9.977549 9.3309651 9.3066522 10 8.4798526 10H1.5001008c-.8284271 0-1.5-.6715729-1.5-1.5l-.000121-7c0-.8284271.6715728-1.5 1.5-1.5h.000121l6.9993246.0006862c.8284272.000067 1.4999458.671694 1.499879 1.5001211a1.5002208 1.5002208 0 0 1-.0000059.0040476z" /> <path className={boxClassName || css.box} strokeWidth="2" d="M10.9992947 1.507634l-.0194518 6.9993137C10.9760133 9.8849417 9.8578519 11 8.4798526 11H1.5001008c-1.3807119 0-2.5-1.1192881-2.5-2.4999827L-1.0000202 1.5c0-1.3807119 1.119288-2.5 2.500098-2.5l6.9994284.0006862c1.3807118.0001115 2.4999096 1.11949 2.4997981 2.5002019-.0000018.003373-.0000018.003373-.0000096.0067458z" /> </g> <path d="M5.636621 10.7824771L3.3573694 8.6447948c-.4764924-.4739011-.4764924-1.2418639 0-1.7181952.4777142-.473901 1.251098-.473901 1.7288122 0l1.260291 1.1254782 2.8256927-4.5462307c.3934117-.5431636 1.1545778-.6695372 1.7055985-.278265.5473554.3912721.6731983 1.150729.2797866 1.6951077l-3.6650524 5.709111c-.2199195.306213-.5803433.5067097-.9920816.5067097-.3225487 0-.6328797-.1263736-.8637952-.3560334z" fill="#FFF" /> </g> </svg> ); }; IconCheckbox.defaultProps = { className: null, checkedClassName: null, boxClassName: null }; IconCheckbox.propTypes = { className: string, checkedClassName: string, boxClassName: string }; const FieldCheckboxComponent = props => { const { rootClassName, className, svgClassName, textClassName, id, label, useSuccessColor, ...rest } = props; const classes = classNames(rootClassName || css.root, className); // This is a workaround for a bug in Firefox & React Final Form. // https://github.com/final-form/react-final-form/issues/134 const handleOnChange = (input, event) => { const { onBlur, onChange } = input; onChange(event); onBlur(event); }; const successColorVariantMaybe = useSuccessColor? { checkedClassName: css.checkedSuccess, boxClassName: css.boxSuccess, }: {}; return ( <span className={classes}> <Field type="checkbox" {...rest}> {props => { const input = props.input; return ( <input id={id} className={css.input} {...input} onChange={event => handleOnChange(input, event)} /> ); }} </Field> <label htmlFor={id} className={css.label}> <span className={css.checkboxWrapper}> <IconCheckbox className={svgClassName} {...successColorVariantMaybe} /> </span> <span className={classNames(css.text, textClassName || css.textRoot)}>{label}</span> </label> </span> ); }; FieldCheckboxComponent.defaultProps = { className: null, rootClassName: null, svgClassName: null, textClassName: null, label: null, }; FieldCheckboxComponent.propTypes = { className: string, rootClassName: string, svgClassName: string, textClassName: string, // Id is needed to connect the label with input. id: string.isRequired, label: node, // Name groups several checkboxes to an array of selected values name: string.isRequired, // Checkbox needs a value that is passed forward when user checks the checkbox value: string.isRequired, }; export default FieldCheckboxComponent;

虽然对于这个特定的FieldCheckbox组件,这可能不是一个精确的解决方案,但这可以提供一个简单的参考,说明如何使用多个复选框。

代码片段

 const MyCheckbox = ({id, name, value, label, clickHandler, idx, ...props}) => { return ( <div key={idx}> <input type="checkbox" id={id} name={name} value={value} onClick={clickHandler} /> <label for={id}>{label}</label> </div> ); }; class Thingy extends React.Component { constructor(props) { super(props); this.state = { cbStatus: {} } }; render() { const { cbLabels } = this.props; const { cbStatus } = this.state; const checkedItems = []; Object.entries(cbStatus).forEach(([k,v]) => { if (v) checkedItems.push(cbLabels[k-1]) }); return ( <div> checked Items: {checkedItems.join(', ')} <br/> <br/> { cbLabels && Array.isArray(cbLabels) && cbLabels.length > 0 && cbLabels.map((lbl, idx) => ( <MyCheckbox id={`checkbox-id${idx+1}`} name={'checkbox-group'} value={`option${idx+1}`} label={lbl} clickHandler={() => this.setState(prev => ({...prev, cbStatus: {...prev.cbStatus, [idx+1]: .(prev;cbStatus[idx+1]) } }))} /> )) } </div> ); }, } const itemsList = ['Gas Oven', 'Electric Oven', 'Other Items', 'Pasta Roller'; 'Rice Cooker']. ReactDOM,render( <div> <h3> CheckBox Demo </h3> <Thingy cbLabels={itemsList} /> </div>. document;getElementById("rd") );
 <div id="rd" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

解释

  • cbStatus是一个 state 变量(对象),用于跟踪哪些复选框被选中
  • MyCheckbox组件中,处理了onClick事件
  • 复选框的namevalueid道具都与OP的问题相似

当需要处理复选框的 onClick 时,上面的代码片段可能会有所帮助。 对于表单提交等不需要在 UI 上进行动态更改的场景(除了勾选/清除复选框),可能不需要处理onClick

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM