我对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几乎没有任何了解。
非常感谢你的帮助。