store 回收问题修复

This commit is contained in:
liaoxuezhi 2019-12-15 17:38:04 +08:00
parent 420aef95ce
commit 8ec9ad0cb3
7 changed files with 144 additions and 115 deletions

View File

@ -56,7 +56,6 @@
"react-addons-update": "15.6.2", "react-addons-update": "15.6.2",
"react-color": "2.13.8", "react-color": "2.13.8",
"react-cropper": "1.0.0", "react-cropper": "1.0.0",
"react-date-range": "0.9.4",
"react-datetime": "2.16.0", "react-datetime": "2.16.0",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-dropzone": "10.1.10", "react-dropzone": "10.1.10",

View File

@ -734,7 +734,7 @@ export default class ComboControl extends React.Component<ComboProps> {
wrapWithPanel: false, wrapWithPanel: false,
mode: subFormMode, mode: subFormMode,
className: cx(`Combo-form`, formClassName), className: cx(`Combo-form`, formClassName),
lazyOnChange: false lazyChange: false
}, },
{ {
index, index,
@ -907,7 +907,7 @@ export default class ComboControl extends React.Component<ComboProps> {
wrapWithPanel: false, wrapWithPanel: false,
mode: multiLine ? subFormMode : 'row', mode: multiLine ? subFormMode : 'row',
className: cx(`Combo-form`, formClassName), className: cx(`Combo-form`, formClassName),
lazyOnChange: false lazyChange: false
}, },
{ {
index, index,
@ -1043,7 +1043,7 @@ export default class ComboControl extends React.Component<ComboProps> {
wrapWithPanel: false, wrapWithPanel: false,
mode: multiLine ? 'normal' : 'row', mode: multiLine ? 'normal' : 'row',
className: cx(`Combo-form`, formClassName), className: cx(`Combo-form`, formClassName),
lazyOnChange: false lazyChange: false
}, },
{ {
disabled: disabled, disabled: disabled,

View File

@ -52,8 +52,14 @@ export default class FormControl extends React.PureComponent<
static defaultProps = {}; static defaultProps = {};
lazyValidate: Function; lazyValidate = debouce(this.validate.bind(this), 250, {
lazyEmitChange: (submitOnChange: boolean) => void; trailing: true,
leading: false
});
lazyEmitChange = debouce(this.emitChange.bind(this), 250, {
trailing: true,
leading: false
});
state = {value: this.props.control.value}; state = {value: this.props.control.value};
componentWillMount() { componentWillMount() {
const { const {
@ -83,14 +89,6 @@ export default class FormControl extends React.PureComponent<
this.setPrinstineValue = this.setPrinstineValue.bind(this); this.setPrinstineValue = this.setPrinstineValue.bind(this);
this.controlRef = this.controlRef.bind(this); this.controlRef = this.controlRef.bind(this);
this.handleBlur = this.handleBlur.bind(this); this.handleBlur = this.handleBlur.bind(this);
this.lazyValidate = debouce(this.validate.bind(this), 250, {
trailing: true,
leading: false
});
this.lazyEmitChange = debouce(this.emitChange.bind(this), 250, {
trailing: true,
leading: false
});
if (!name) { if (!name) {
return; return;
@ -165,43 +163,43 @@ export default class FormControl extends React.PureComponent<
const props = this.props; const props = this.props;
const form = nextProps.formStore; const form = nextProps.formStore;
if (!nextProps.control.name) { // if (!nextProps.control.name) {
// 把 name 删了, 对 model 做清理 // // 把 name 删了, 对 model 做清理
this.model && this.disposeModel(); // this.model && this.disposeModel();
this.reaction && this.reaction(); // this.reaction && this.reaction();
this.model = undefined; // this.model = undefined;
return; // return;
} else if (nextProps.control.name !== props.control.name || !this.model) { // } else if (nextProps.control.name !== props.control.name || !this.model) {
// 对 model 做清理 // // 对 model 做清理
this.model && this.disposeModel(); // this.model && this.disposeModel();
this.reaction && this.reaction(); // this.reaction && this.reaction();
// name 是后面才有的,比如编辑模式下就会出现。 // // name 是后面才有的,比如编辑模式下就会出现。
const model = (this.model = form.registryItem(nextProps.control.name, { // const model = (this.model = form.registryItem(nextProps.control.name, {
id: nextProps.control.id, // id: nextProps.control.id,
type: nextProps.control.type, // type: nextProps.control.type,
required: nextProps.control.required, // required: nextProps.control.required,
unique: nextProps.control.unique, // unique: nextProps.control.unique,
value: nextProps.control.value, // value: nextProps.control.value,
rules: nextProps.control.validations, // rules: nextProps.control.validations,
multiple: nextProps.control.multiple, // multiple: nextProps.control.multiple,
delimiter: nextProps.control.delimiter, // delimiter: nextProps.control.delimiter,
valueField: nextProps.control.valueField, // valueField: nextProps.control.valueField,
labelField: nextProps.control.labelField, // labelField: nextProps.control.labelField,
joinValues: nextProps.control.joinValues, // joinValues: nextProps.control.joinValues,
extractValue: nextProps.control.extractValue, // extractValue: nextProps.control.extractValue,
messages: nextProps.control.validationErrors // messages: nextProps.control.validationErrors
})); // }));
// this.forceUpdate(); // // this.forceUpdate();
this.setState({ // this.setState({
value: model.value // value: model.value
}); // });
this.reaction = reaction( // this.reaction = reaction(
() => model.value, // () => model.value,
value => this.setState({value}) // value => this.setState({value})
); // );
} // }
if ( if (
this.model && this.model &&
@ -268,10 +266,11 @@ export default class FormControl extends React.PureComponent<
componentWillUnmount() { componentWillUnmount() {
this.hook && this.props.removeHook(this.hook); this.hook && this.props.removeHook(this.hook);
this.hook2 && this.props.removeHook(this.hook2); this.hook2 && this.props.removeHook(this.hook2);
this.disposeModel(); this.lazyValidate.cancel();
// this.lazyEmitChange.flush();
this.lazyEmitChange.cancel();
this.reaction && this.reaction(); this.reaction && this.reaction();
(this.lazyValidate as any).cancel(); this.disposeModel();
(this.lazyEmitChange as any).cancel();
} }
disposeModel() { disposeModel() {
@ -348,7 +347,7 @@ export default class FormControl extends React.PureComponent<
const { const {
formStore: form, formStore: form,
onChange, onChange,
control: {type, pipeOut}, control: {type, pipeOut, changeImmediately: conrolChangeImmediately},
formInited formInited
} = this.props; } = this.props;
@ -368,7 +367,7 @@ export default class FormControl extends React.PureComponent<
value value
}, },
() => () =>
changeImmediately || !formInited changeImmediately || conrolChangeImmediately || !formInited
? this.emitChange(submitOnChange) ? this.emitChange(submitOnChange)
: this.lazyEmitChange(submitOnChange) : this.lazyEmitChange(submitOnChange)
); );
@ -521,7 +520,6 @@ export default class FormControl extends React.PureComponent<
return render('', control, { return render('', control, {
...rest, ...rest,
key: `${control.name || ''}-${control.type}`, // 很重要:如果不写实际的 control 组件变了,但是 this.control 还是引用的原来那个。
defaultSize: controlWidth, defaultSize: controlWidth,
disabled: disabled || control.disabled, disabled: disabled || control.disabled,
formItem: model, formItem: model,

View File

@ -152,7 +152,7 @@ export default class Form extends React.Component<FormProps, object> {
'onFailed', 'onFailed',
'onFinished', 'onFinished',
'canAccessSuperData', 'canAccessSuperData',
'lazyOnChange' 'lazyChange'
]; ];
hooks: { hooks: {
@ -163,7 +163,10 @@ export default class Form extends React.Component<FormProps, object> {
shouldLoadInitApi: boolean = false; shouldLoadInitApi: boolean = false;
timer: NodeJS.Timeout; timer: NodeJS.Timeout;
mounted: boolean; mounted: boolean;
lazyHandleChange: (value: any, name: string, submit: boolean) => void; lazyHandleChange = debouce(this.handleChange.bind(this), 250, {
trailing: true,
leading: false
});
constructor(props: FormProps) { constructor(props: FormProps) {
super(props); super(props);
@ -179,10 +182,6 @@ export default class Form extends React.Component<FormProps, object> {
this.addHook = this.addHook.bind(this); this.addHook = this.addHook.bind(this);
this.removeHook = this.removeHook.bind(this); this.removeHook = this.removeHook.bind(this);
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
this.lazyHandleChange = debouce(this.handleChange, 250, {
trailing: true,
leading: false
});
this.renderFormItems = this.renderFormItems.bind(this); this.renderFormItems = this.renderFormItems.bind(this);
this.reload = this.reload.bind(this); this.reload = this.reload.bind(this);
this.silentReload = this.silentReload.bind(this); this.silentReload = this.silentReload.bind(this);
@ -303,7 +302,8 @@ export default class Form extends React.Component<FormProps, object> {
componentWillUnmount() { componentWillUnmount() {
this.mounted = false; this.mounted = false;
clearTimeout(this.timer); clearTimeout(this.timer);
(this.lazyHandleChange as any).cancel(); // this.lazyHandleChange.flush();
this.lazyHandleChange.cancel();
this.asyncCancel && this.asyncCancel(); this.asyncCancel && this.asyncCancel();
this.disposeOnValidate && this.disposeOnValidate(); this.disposeOnValidate && this.disposeOnValidate();
const store = this.props.store; const store = this.props.store;
@ -896,13 +896,15 @@ export default class Form extends React.Component<FormProps, object> {
disabled, disabled,
controlWidth, controlWidth,
resolveDefinitions, resolveDefinitions,
lazyOnChange lazyChange
} = props; } = props;
const subProps = { const subProps = {
formStore: form, formStore: form,
data: store.data, data: store.data,
key, key: `${(control as Schema).name || ''}-${
(control as Schema).type
}-${key}`,
formInited: form.inited, formInited: form.inited,
formMode: mode, formMode: mode,
formHorizontal: horizontal, formHorizontal: horizontal,
@ -910,8 +912,7 @@ export default class Form extends React.Component<FormProps, object> {
disabled: disabled || (control as Schema).disabled || form.loading, disabled: disabled || (control as Schema).disabled || form.loading,
btnDisabled: form.loading || form.validating, btnDisabled: form.loading || form.validating,
onAction: this.handleAction, onAction: this.handleAction,
onChange: onChange: this.lazyHandleChange,
lazyOnChange !== false ? this.lazyHandleChange : this.handleChange,
addHook: this.addHook, addHook: this.addHook,
removeHook: this.removeHook, removeHook: this.removeHook,
renderFormItems: this.renderFormItems, renderFormItems: this.renderFormItems,
@ -919,7 +920,7 @@ export default class Form extends React.Component<FormProps, object> {
}; };
const subSchema: any = const subSchema: any =
control && (control as Schema).type === 'control' (control as Schema).type === 'control'
? control ? control
: { : {
type: 'control', type: 'control',
@ -944,6 +945,7 @@ export default class Form extends React.Component<FormProps, object> {
control.hiddenOn && (subSchema.hiddenOn = control.hiddenOn); control.hiddenOn && (subSchema.hiddenOn = control.hiddenOn);
control.visibleOn && (subSchema.visibleOn = control.visibleOn); control.visibleOn && (subSchema.visibleOn = control.visibleOn);
lazyChange === false && (control.changeImmediately = true);
} }
return render(`${region ? `${region}/` : ''}${key}`, subSchema, subProps); return render(`${region ? `${region}/` : ''}${key}`, subSchema, subProps);
@ -956,46 +958,23 @@ export default class Form extends React.Component<FormProps, object> {
controls, controls,
mode, mode,
className, className,
classnames: cx
} = this.props;
return (
<div className={cx(`Form`, `Form--${mode || 'normal'}`, className)}>
{this.renderFormItems({
tabs,
fieldSet,
controls
})}
</div>
);
}
render() {
const {
className,
wrapWithPanel,
render,
title,
store,
panelClassName,
debug,
headerClassName,
footerClassName,
actionsClassName,
bodyClassName,
classPrefix: ns,
classnames: cx, classnames: cx,
debug,
$path, $path,
affixFooter, store,
mode render
} = this.props; } = this.props;
const WrapperComponent = const WrapperComponent =
this.props.wrapperComponent || this.props.wrapperComponent ||
(/(?:\/|^)form\//.test($path as string) ? 'div' : 'form'); (/(?:\/|^)form\//.test($path as string) ? 'div' : 'form');
let body = ( return (
<WrapperComponent onSubmit={this.handleFormSubmit} noValidate> <WrapperComponent
className={cx(`Form`, `Form--${mode || 'normal'}`, className)}
onSubmit={this.handleFormSubmit}
noValidate
>
{debug ? ( {debug ? (
<pre> <pre>
<code>{JSON.stringify(store.data, null, 2)}</code> <code>{JSON.stringify(store.data, null, 2)}</code>
@ -1004,7 +983,11 @@ export default class Form extends React.Component<FormProps, object> {
<Spinner show={store.loading} overlay /> <Spinner show={store.loading} overlay />
{this.renderBody()} {this.renderFormItems({
tabs,
fieldSet,
controls
})}
{render( {render(
'modal', 'modal',
@ -1039,6 +1022,24 @@ export default class Form extends React.Component<FormProps, object> {
)} )}
</WrapperComponent> </WrapperComponent>
); );
}
render() {
const {
wrapWithPanel,
render,
title,
store,
panelClassName,
headerClassName,
footerClassName,
actionsClassName,
bodyClassName,
classnames: cx,
affixFooter
} = this.props;
let body: JSX.Element = this.renderBody();
if (wrapWithPanel) { if (wrapWithPanel) {
body = render( body = render(
@ -1101,6 +1102,8 @@ export class FormRenderer extends Form {
componentWillUnmount() { componentWillUnmount() {
const scoped = this.context as IScopedContext; const scoped = this.context as IScopedContext;
scoped.unRegisterComponent(this); scoped.unRegisterComponent(this);
super.componentWillUnmount();
} }
doAction(action: Action, data: object, throwErrors: boolean = false) { doAction(action: Action, data: object, throwErrors: boolean = false) {

View File

@ -42,7 +42,7 @@ export interface FlvSourceProps {
order?: number; order?: number;
} }
let currentPlaying: any = null; // let currentPlaying: any = null;
export class FlvSource extends React.Component<FlvSourceProps, any> { export class FlvSource extends React.Component<FlvSourceProps, any> {
flvPlayer: any; flvPlayer: any;
@ -262,17 +262,17 @@ export default class Video extends React.Component<VideoProps, VideoState> {
videoState: state videoState: state
}); });
if (!state.paused) { // if (!state.paused) {
if ( // if (
currentPlaying && // currentPlaying &&
currentPlaying.video && // currentPlaying.video &&
currentPlaying !== player // currentPlaying !== player
) { // ) {
currentPlaying.pause(); // currentPlaying.pause();
} // }
currentPlaying = player; // currentPlaying = player;
} // }
if (!this.frameDom || !this.times) { if (!this.frameDom || !this.times) {
return; return;

View File

@ -1,4 +1,4 @@
import {types, getRoot, Instance} from 'mobx-state-tree'; import {types, getRoot, Instance, destroy} from 'mobx-state-tree';
import {extendObject, createObject} from '../utils/helper'; import {extendObject, createObject} from '../utils/helper';
import {IRendererStore} from './index'; import {IRendererStore} from './index';
import {dataMapping} from '../utils/tpl-builtin'; import {dataMapping} from '../utils/tpl-builtin';
@ -13,7 +13,9 @@ export const iRendererStore = types
data: types.optional(types.frozen(), {}), data: types.optional(types.frozen(), {}),
updatedAt: 0, // 从服务端更新时刻 updatedAt: 0, // 从服务端更新时刻
pristine: types.optional(types.frozen(), {}), pristine: types.optional(types.frozen(), {}),
parentId: types.optional(types.string, ''), disposed: false,
parentId: '',
childrenIds: types.optional(types.array(types.string), []),
action: types.optional(types.frozen(), undefined), action: types.optional(types.frozen(), undefined),
dialogOpen: false, dialogOpen: false,
dialogData: types.optional(types.frozen(), undefined), dialogData: types.optional(types.frozen(), undefined),
@ -35,6 +37,18 @@ export const iRendererStore = types
.actions(self => { .actions(self => {
const dialogCallbacks = new SimpleMap<(result?: any) => void>(); const dialogCallbacks = new SimpleMap<(result?: any) => void>();
function dispose() {
// 先标记自己是要销毁的。
self.disposed = true;
const parent = self.parentStore;
if (!self.childrenIds.length) {
const id = self.id;
destroy(self);
parent && parent.onChildDispose(id);
}
}
return { return {
initData(data: object = {}) { initData(data: object = {}) {
self.pristine = data; self.pristine = data;
@ -152,7 +166,16 @@ export const iRendererStore = types
dialogCallbacks.delete(self.drawerData); dialogCallbacks.delete(self.drawerData);
setTimeout(() => callback(result), 200); setTimeout(() => callback(result), 200);
} }
} },
onChildDispose(childId: string) {
const childrenIds = self.childrenIds.filter(item => item !== childId);
self.childrenIds.replace(childrenIds);
self.disposed && dispose();
},
dispose
}; };
}); });

View File

@ -68,12 +68,18 @@ export const RendererStore = types
if (self.stores.has(store.id as string)) { if (self.stores.has(store.id as string)) {
return self.stores.get(store.id) as IIRendererStore; return self.stores.get(store.id) as IIRendererStore;
} }
if (store.parentId) {
const parent = self.stores.get(store.parentId) as IIRendererStore;
parent.childrenIds.push(store.id);
}
self.stores.put(store); self.stores.put(store);
return self.stores.get(store.id) as IIRendererStore; return self.stores.get(store.id) as IIRendererStore;
}, },
removeStore(store: IIRendererStore) { removeStore(store: IIRendererStore) {
detach(store); store.dispose();
} }
})); }));