Browse Source

feat: reducer 引入 immer 管理

master
suanmei 5 years ago
committed by 拾邑
parent
commit
adcbc04d35
  1. 5
      packages/json-schema-editor/package-lock.json
  2. 1
      packages/json-schema-editor/package.json
  3. 6
      packages/json-schema-editor/src/components/field-input.tsx
  4. 17
      packages/json-schema-editor/src/components/schema-item.tsx
  5. 210
      packages/json-schema-editor/src/reducer/schema.ts

5
packages/json-schema-editor/package-lock.json

@ -619,6 +619,11 @@
"@babel/runtime": "^7.3.1"
}
},
"immer": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/immer/-/immer-6.0.5.tgz",
"integrity": "sha512-Q2wd90qrgFieIpLzAO2q9NLEdmyp/sr76Ml4Vm5peUKgyTa2CQa3ey8zuzwSKOlKH7grCeGBGUcLLVCVW1aguA=="
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",

1
packages/json-schema-editor/package.json

@ -59,6 +59,7 @@
"gitHead": "60b114ff262513544204f33a9bbcb6220d6c1c5d",
"dependencies": {
"i18next": "^19.4.4",
"immer": "^6.0.5",
"lodash-es": "^4.17.15",
"react-i18next": "^11.4.0",
"react-tracked": "^1.3.0"

6
packages/json-schema-editor/src/components/field-input.tsx

@ -15,7 +15,8 @@ const CustomInput = styled(Input)`
}
`;
function FieldInput({ value: initValue, changeField, addonAfter }: FieldInputProps): JSX.Element {
function FieldInput(props: FieldInputProps, ref: React.Ref<Input>): JSX.Element {
const { value: initValue, changeField, addonAfter } = props;
const [value, setValue] = React.useState(initValue);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
@ -37,6 +38,7 @@ function FieldInput({ value: initValue, changeField, addonAfter }: FieldInputPro
return (
<CustomInput
ref={ref}
value={value}
onChange={handleChange}
onBlur={handleBlur}
@ -46,4 +48,4 @@ function FieldInput({ value: initValue, changeField, addonAfter }: FieldInputPro
);
}
export default FieldInput;
export default React.forwardRef(FieldInput);

17
packages/json-schema-editor/src/components/schema-item.tsx

@ -1,4 +1,5 @@
/** @jsx jsx */
import { useRef } from 'react';
import { jsx } from '@emotion/core';
import { Row, Col, Tooltip, Input, Checkbox, Select } from 'antd';
import { useTranslation } from 'react-i18next';
@ -34,6 +35,8 @@ function SchemaFormItem({
keyRoute,
children,
}: SchemaItemProps): JSX.Element {
const fieldRef = useRef<Input>(null);
const { t } = useTranslation();
const schemaDispatch = useSchemaDispatch();
const setUIState = useSetUIState();
@ -46,10 +49,15 @@ function SchemaFormItem({
};
const toggleRequired = (): void => {
if (!fieldRef.current) return;
const newField = fieldRef.current.state.value;
const fieldKeyRoute = keyRoute.slice(0, -1).concat(newField);
if (required) {
schemaDispatch({ type: 'REMOVE_REQUIRED', keyRoute });
schemaDispatch({ type: 'REMOVE_REQUIRED', keyRoute: fieldKeyRoute });
} else {
schemaDispatch({ type: 'ADD_REQUIRED', keyRoute });
schemaDispatch({ type: 'ADD_REQUIRED', keyRoute: fieldKeyRoute });
}
};
@ -66,9 +74,7 @@ function SchemaFormItem({
};
const changeField = (value: string): void => {
setTimeout(() => {
schemaDispatch({ type: 'CHANGE_FIELD', keyRoute, field: value });
}, 500);
schemaDispatch({ type: 'CHANGE_FIELD', keyRoute, field: value });
};
const changeType = (value: SchemaType): void => {
@ -94,6 +100,7 @@ function SchemaFormItem({
</Col>
<Col span="22">
<FieldInput
ref={fieldRef}
value={field}
changeField={changeField}
addonAfter={

210
packages/json-schema-editor/src/reducer/schema.ts

@ -1,5 +1,6 @@
import produce, { Draft } from 'immer';
import { get, set, unset, remove, cloneDeep } from 'lodash-es';
import { SchemaType, SchemaState, SchemaAction, KeyRoute } from '../model';
import { SchemaType, SchemaItem, SchemaState, SchemaAction, KeyRoute } from '../model';
import { defaultSchema, setAllFieldRequired } from '../utils/schema';
let fieldNum = 0;
@ -8,169 +9,154 @@ const setSchema = (schema: SchemaState): SchemaState => {
return schema;
};
const addRequired = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
const schema = cloneDeep(state);
const field = keyRoute[keyRoute.length - 1];
const addRequired = (state: SchemaState, keyRoute: KeyRoute): void => {
const field = keyRoute.slice(-1)[0];
const requiredKeyRoute = keyRoute.slice(0, -2).concat('required');
const required = get(schema, requiredKeyRoute) || [];
const required = get(state, requiredKeyRoute) || [];
required.push(field);
set(schema, requiredKeyRoute, required);
return schema;
set(state, requiredKeyRoute, required);
};
const removeRequired = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
const schema = cloneDeep(state);
const field = keyRoute[keyRoute.length - 1];
const removeRequired = (state: SchemaState, keyRoute: KeyRoute): void => {
const field = keyRoute.slice(-1)[0];
const requiredKeyRoute = keyRoute.slice(0, -2).concat('required');
const required = get(schema, requiredKeyRoute) || [];
const required = get(state, requiredKeyRoute) || [];
remove(required, (item) => item === field);
set(schema, requiredKeyRoute, required);
return schema;
set(state, requiredKeyRoute, required);
};
const toggleAllChecked = (state: SchemaState, checked: boolean): SchemaState => {
const schema = cloneDeep(state);
setAllFieldRequired(schema, checked);
return schema;
const toggleAllChecked = (state: SchemaState, checked: boolean): void => {
setAllFieldRequired(state, checked);
};
const addChildField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
const addChildField = (state: Draft<SchemaState>, keyRoute: KeyRoute): void => {
fieldNum += 1;
const newField = `field_${fieldNum}`;
let schema = cloneDeep(state);
// add new field - string
const newKeyRoute = keyRoute.concat(newField);
set(schema, newKeyRoute, defaultSchema.string);
set(state, newKeyRoute, defaultSchema.string);
// add new filed to its parent's required
schema = addRequired(schema, newKeyRoute);
return schema;
addRequired(state, newKeyRoute);
};
const addSiblingField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
const addSiblingField = (state: SchemaState, keyRoute: KeyRoute): void => {
fieldNum += 1;
const newField = `field_${fieldNum}`;
let schema = cloneDeep(state);
// add new field - string
const newKeyRoute = keyRoute.slice(0, -1).concat(newField);
set(schema, newKeyRoute, defaultSchema.string);
set(state, newKeyRoute, defaultSchema.string);
// add new filed to its parent's required
schema = addRequired(schema, newKeyRoute);
return schema;
addRequired(state, newKeyRoute);
};
const removeField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
let schema = cloneDeep(state);
const removeField = (state: SchemaState, keyRoute: KeyRoute): void => {
// remove field
unset(schema, keyRoute);
unset(state, keyRoute);
// remove filed from its parent's required
schema = removeRequired(schema, keyRoute);
return schema;
removeRequired(state, keyRoute);
};
const changeField = (state: SchemaState, keyRoute: KeyRoute, field: string): SchemaState => {
let schema = cloneDeep(state);
const changeField = (state: SchemaState, keyRoute: KeyRoute, newField: string): void => {
const oldField = keyRoute.slice(-1)[0];
const parentKeyRoute = keyRoute.slice(0, -1);
const properties = get(state, parentKeyRoute);
const newProperties: { [key: string]: SchemaItem } = {};
// keep the order of fields
// eslint-disable-next-line no-restricted-syntax
for (const field in properties) {
if (field === oldField) {
newProperties[newField] = properties[field];
} else {
newProperties[field] = properties[field];
}
}
// temp old field's value and remove old field
const temp = get(schema, keyRoute);
unset(schema, keyRoute);
set(state, parentKeyRoute, newProperties);
// remove old filed from its parent's required
schema = removeRequired(schema, keyRoute);
// point to new field and add temp to it
const fieldKeyRoute = keyRoute.slice(0, -1).concat(field);
set(schema, fieldKeyRoute, temp);
removeRequired(state, keyRoute);
// add new filed to its parent's required
schema = addRequired(schema, fieldKeyRoute);
return schema;
const fieldKeyRoute = parentKeyRoute.concat(newField);
addRequired(state, fieldKeyRoute);
};
const changeType = (state: SchemaState, keyRoute: KeyRoute, fieldType: SchemaType): SchemaState => {
const schema = cloneDeep(state);
const changeType = (state: SchemaState, keyRoute: KeyRoute, fieldType: SchemaType): void => {
// set field new value
set(schema, keyRoute, defaultSchema[fieldType]);
return schema;
set(state, keyRoute, defaultSchema[fieldType]);
};
const changeTitle = (state: SchemaState, keyRoute: KeyRoute, title: string): SchemaState => {
const schema = cloneDeep(state);
const changeTitle = (state: SchemaState, keyRoute: KeyRoute, title: string): void => {
// point to title and set new value
const titleKeyRoute = keyRoute.concat('title');
set(schema, titleKeyRoute, title);
return schema;
set(state, titleKeyRoute, title);
};
const changeDesc = (state: SchemaState, keyRoute: KeyRoute, desc: string): SchemaState => {
const schema = cloneDeep(state);
const changeDesc = (state: SchemaState, keyRoute: KeyRoute, desc: string): void => {
// point to desc and set new value
const descKeyRoute = keyRoute.concat('description');
set(schema, descKeyRoute, desc);
return schema;
set(state, descKeyRoute, desc);
};
const reducer = (state: SchemaState, action: SchemaAction): SchemaState => {
switch (action.type) {
case 'SET_SCHEMA':
return setSchema(action.schema);
case 'ADD_CHILD_FIELD':
return addChildField(state, action.keyRoute);
case 'ADD_SIBLING_FIELD':
return addSiblingField(state, action.keyRoute);
case 'REMOVE_FIELD':
return removeField(state, action.keyRoute);
case 'TOGGLE_ALL_CHECKED':
return toggleAllChecked(state, action.checked);
case 'ADD_REQUIRED':
return addRequired(state, action.keyRoute);
case 'REMOVE_REQUIRED':
return removeRequired(state, action.keyRoute);
case 'CHANGE_FIELD':
return changeField(state, action.keyRoute, action.field);
case 'CHANGE_TYPE':
return changeType(state, action.keyRoute, action.fieldType);
case 'CHANGE_TITLE':
return changeTitle(state, action.keyRoute, action.title);
case 'CHANGE_DESC':
return changeDesc(state, action.keyRoute, action.desc);
default:
return state;
}
};
const reducer = (state: SchemaState, action: SchemaAction): SchemaState =>
produce(state, (draft: Draft<SchemaState>) => {
switch (action.type) {
case 'SET_SCHEMA':
setSchema(action.schema);
break;
case 'ADD_CHILD_FIELD':
addChildField(draft, action.keyRoute);
break;
case 'ADD_SIBLING_FIELD':
addSiblingField(draft, action.keyRoute);
break;
case 'REMOVE_FIELD':
removeField(draft, action.keyRoute);
break;
case 'TOGGLE_ALL_CHECKED':
toggleAllChecked(draft, action.checked);
break;
case 'ADD_REQUIRED':
addRequired(draft, action.keyRoute);
break;
case 'REMOVE_REQUIRED':
removeRequired(draft, action.keyRoute);
break;
case 'CHANGE_FIELD':
changeField(draft, action.keyRoute, action.field);
break;
case 'CHANGE_TYPE':
changeType(draft, action.keyRoute, action.fieldType);
break;
case 'CHANGE_TITLE':
changeTitle(draft, action.keyRoute, action.title);
break;
case 'CHANGE_DESC':
changeDesc(draft, action.keyRoute, action.desc);
break;
default:
break;
}
});
export default reducer;

Loading…
Cancel
Save