ReactJS:尝试在子组件的表单更改后设置状态

时间:2017-09-14 03:52:19

标签: javascript jquery reactjs

我对ReactJS非常陌生。目前我被要求处理我以前的同事所做的项目,他离开了,没有人知道它是如何工作的,我也不能问任何人。
无论如何,我被要求在用户尝试重定向时添加一个确认对话框,但是他在表单上更改了一些值。
页面的结构有点复杂。 AFAIK,它是一个有不同孩子的父母,每个孩子都有自己的形式。家长本身没有。我设法找出每个孩子使用可变的'pristine'(PropType.bool.isRequired)来确定表单是否已被更改,我还设法将'pristine'的值检索回父级。
我当前的方法:每当孩子的表单发生变化时,它都会调用来自父级的函数,父级将使用setState来保存“pristine”的值。当用户重定向时,我注意到它会触发componentWillUnmount,所以我打算在这里进行确认对话。如果状态中的值为true,则会提示确认对话框 我的问题:网络浏览器的控制台不断发出警告:

  

警告:setState(...):无法在现有状态转换期间更新(例如在render或其他组件的构造函数中)。渲染方法应该是道具和状态的纯函数;构造函数副作用是反模式,但可以移动到componentWillMount

另外,州内的价值总是落后一步。

从过去两天的“清理”开始,由于孩子处于'渲染'状态,在'render'中执行setState会导致'render'的无限循环,这不是一个好方法。

以下是相关的代码:
index.jsx

class PageEditProperty extends React.PureComponent {

constructor(props){
    super(props);

    this.SetCredentialChanged = this.SetCredentialChanged.bind(this);
    this.onUnload = this.onUnload.bind(this);
}
state = {
    deleteModalOpen: false,
    credentialsChanged: false,
  };

openDeleteModal = () => {
this.setState({ deleteModalOpen: true });
};

closeDeleteModal = () => {
this.setState({ deleteModalOpen: false });
};

SetCredentialChanged = (pristine) =>{
    this.setState({credentialsChanged: pristine},()=>{console.log(this.state.credentialsChanged);});
};

onUnload(){
  //console.log("Redirect");
  if(this.state.credentialsChanged){
    confirm("Hello");
  }
}

componentDidMount(){
  window.addEventListener("beforeunload", this.onUnload);
  //this.setState({credentialsChanged: false});
  console.log("Mount");
}

componentWillUnmount(){
    window.removeEventListener("beforeunload", this.onUnload);
    console.log("Unmount");
    console.log(this.state.credentialsChanged);
 }

render() {
const childProps = {
  propertyId: this.props.propertyId,
  propertyExists: this.props.propertyExists,
  clientPropertyId: this.props.clientPropertyId,
  beforeSubmit: this.props.createPropertyIfNeeded,
  setPendingRequest: this.props.setPendingRequest,
};
return (
  <div className="edit-property container">
    <div className="mdc-layout-grid">
      <div className="mdc-layout-grid__inner">
        <div className="mdc-layout-grid__cell--span-12">
          <PageHeader {...childProps} onDeleteClick={this.openDeleteModal} />
        </div>
        <div className="mdc-layout-grid__cell--span-6 mdc-layout-grid__cell--span-6-phone mdc-layout-grid__cell--span-8-tablet">
          <div className="mdc-elevation--z1 mdc-theme--background">
            <ThumbnailUploader {...childProps} />
          </div>
        </div>
        <div className="mdc-layout-grid__cell--span-6 mdc-layout-grid__cell--span-8-tablet">
          <div className="mdc-elevation--z1 mdc-theme--background">
            <PropertyInfoEdit {...childProps} onChangedCredentials={this.SetCredentialChanged}/>
          </div>
        </div>
        <div className="mdc-layout-grid__cell--span-4 mdc-layout-grid__cell--span-8-tablet">
          <div className="mdc-elevation--z1 mdc-theme--background">
            <TripodUploader {...childProps} />
          </div>
        </div>
        <div className="mdc-layout-grid__cell--span-8 mdc-layout-grid__cell--span-8-tablet">
          <div className="mdc-elevation--z1 mdc-theme--background">
            <PropertyLocationEdit {...childProps} />
          </div>
        </div>
        <div className="mdc-layout-grid__cell--span-12 mdc-layout-grid__cell--span-8-tablet">
          <div className="mdc-elevation--z1 mdc-theme--background">
            <ScenesListEdit {...childProps} />
          </div>
        </div>
      </div>
    </div>
    <Dialog
      open={this.state.deleteModalOpen}
      acceptLabel={this.props.t('Delete this content')}
      cancelLabel={this.props.t('Cancel')}
      acceptClassname="danger-bg"
      onAccept={this.props.deleteProperty}
      onClose={this.closeDeleteModal}
      title={this.props.t('Delete this content ?')}
    >
      {this.props.t('property-delete-text')}
    </Dialog>
  </div>
    );
  }
}

PropertyInfoEdit.jsx:

PropertyInfoEdit.propTypes = {
  pristine: PropTypes.bool.isRequired,
  submitting: PropTypes.bool.isRequired,
  valid: PropTypes.bool.isRequired,
  reset: PropTypes.func.isRequired,
  handleSubmit: PropTypes.func.isRequired,
    onChangedCredentials: PropTypes.func,
};
PropertyInfoEdit.defaultTypes = {
  pending: false,
};
function PropertyInfoEdit({
  onSubmit,
  handleSubmit,
  pristine,
  reset,
  submitting,
  valid,
  pending,
  categories,
  t,
  onChangedCredentials,
}) {

    function ChangeCredentials(e){
        {onChangedCredentials(e)};
    }

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="edit-property__form" onChange={onChangedCredentials(pristine)}>
      <div className="mdc-card__primary form-group">
        <Field name="nameEn" label={t('English name')} component={renderTextField} type="text"/>
        <Field name="nameCh" label={t('Chinese name')} component={renderTextField} type="text"/>
        <Field
          name="categoryId"
          label={t('Category')}
          component={renderSelectField}
          fullWidth
          metaText={false}
          options={categories.map(c => ({
            value: c.id,
            name:
              t('_locale') === 'zh-CN'
                ? c.nameZhCN
                : t('_locale') === 'zh-TW' ? c.nameZhTW : c.nameEn,
          }))}
        />
      </div>

      <div className="mdc-card__actions" style={{ justifyContent: 'center' }}>
        <div className="inline-button-group">
          <RaisedButton
            type="button"
            onClick={reset}
            disabled={pristine || submitting}
            className="margin-auto mdc-button--secondary"
          >
            {t('Reset')}
          </RaisedButton>
          <RaisedButton
            disabled={pristine || submitting || !valid}
            type="submit"
            className="margin-auto mdc-button--primary"
          >
            {(pending && <i className="material-icons spin2s">cached</i>) || t('Submit')}
          </RaisedButton>
        </div>
      </div>
    </form>
  );
}

// region HOC Declarations

/**
 * Injects the property basic information data in the component props
 * @type {ComponentDecorator}
 */
const injectInfoData = injectQuery(
  gql`
    query PropertyInfoQuery($id: String) {
      property(id: $id) {
        id
        nameCh
        nameEn
        categoryId
      }
    }
  `,
  {
    options: ({ propertyId, propertyExists }) => ({
      variables: { id: propertyId },
      fetchPolicy: propertyExists ? 'network-only' : 'cache-only',
    }),
    props: ({ data }) => ({
      property: data.property,
    }),
  },
);

/**
 * Injects mutation handler to change the basic informations data
 */
const PropertyInfoUpdateMutation = gql`
  mutation PropertyInfoUpdate($id: String!, $property: PropertyUpdate) {
    updateProperty(id: $id, property: $property, autoPublish: false) {
      id
      nameCh
      nameEn
      categoryId
      published
      isValid
    }
  }
`;

const injectInfoMutation = injectQuery(PropertyInfoUpdateMutation, {
  props: ({ ownProps, mutate }) => ({
    updateDBProperty: property =>
      mutate({
        variables: { id: ownProps.propertyId, property },
      }),
  }),
});

/**
 * Injects the categories
 * @type {ComponentDecorator}
 */
const injectCategories = injectQuery(
  gql`
    query PagePropertyEditCategoryQuery {
      categories(noFilter: true) {
        id
        nameEn
        nameZhCN
        nameZhTW
      }
    }
  `,
  {
    props: ({ data }) => ({
      categories: data.categories || [],
    }),
  },
);

const injectSubmitHandler = withBoundHandlers({
  onSubmit(data) {
    return this.props
      .beforeSubmit()
      .then(() => this.props.propertyExistsPromise)
      .then(() => this.props.updateDBProperty(data));
  },
});

/**
 * Create and wraps a reduxForm around our component
 * @type {function}
 */
const wrapsWithReduxForm = compose(
  withProps(props => ({
    form: `PropertyInfoForm#${props.clientPropertyId}`,
    initialValues: props.property
      ? {
          nameEn: props.property.nameEn || '',
          nameCh: props.property.nameCh || '',
          categoryId: props.property.categoryId,
        }
      : undefined,
  })),
  reduxForm({
    enableReinitialize: true,
    keepDirtyOnReinitialize: true,
    destroyOnUnmount: false,
  }),
);

// endregion HOC Declarations

// @type {React.Component}
const DecoratedPropertyInfoEdit = compose(
  injectInfoData,
  injectCategories,
  injectInfoMutation,
  wrapsWithReduxForm,
  makePromiseFor(
    // 1) the property must exist on the server and
    // 2) it must have loaded in this component (ex we have its id)
    props => props.propertyExists && props.property && props.property.id,
    promise => ({ propertyExistsPromise: promise }),
  ),
  injectSubmitHandler,
  translate(),
)(PropertyInfoEdit);
DecoratedPropertyInfoEdit.propTypes = {
  propertyId: PropTypes.string,
  clientPropertyId: PropTypes.string.isRequired,
  propertyExists: PropTypes.bool,
  beforeSubmit: PropTypes.func.isRequired,
  onChangedCredentials: PropTypes.func,
};

export default DecoratedPropertyInfoEdit;

我理解我在这里做错了什么,但我不知道如何解决它。所以,如果有人可以帮助我,那将是很好的,因为我对React / Redux几乎没有任何了解。

非常感谢你的帮助。

0 个答案:

没有答案