simplifies useFieldType and implements a way to retrieve data by path in useForm
This commit is contained in:
@@ -86,23 +86,23 @@ const Form = (props) => {
|
||||
return reduceFieldsToValues(siblingFields);
|
||||
}, [fields]);
|
||||
|
||||
const countRows = useCallback((rowName) => {
|
||||
const namePrefixToRemove = rowName.substring(0, rowName.lastIndexOf('.') + 1);
|
||||
const getDataByPath = useCallback((path) => {
|
||||
const pathPrefixToRemove = path.substring(0, path.lastIndexOf('.') + 1);
|
||||
|
||||
const rows = Object.keys(fields).reduce((matchedRows, key) => {
|
||||
if (key.indexOf(`${rowName}.`) === 0) {
|
||||
if (key.indexOf(`${path}.`) === 0) {
|
||||
return {
|
||||
...matchedRows,
|
||||
[key.replace(namePrefixToRemove, '')]: fields[key],
|
||||
[key.replace(pathPrefixToRemove, '')]: fields[key],
|
||||
};
|
||||
}
|
||||
|
||||
return matchedRows;
|
||||
}, {});
|
||||
|
||||
const unflattenedRows = unflatten(rows);
|
||||
const rowCount = unflattenedRows[rowName.replace(namePrefixToRemove, '')]?.length || 0;
|
||||
return rowCount;
|
||||
const rowValues = reduceFieldsToValues(rows);
|
||||
const unflattenedRows = unflatten(rowValues);
|
||||
return unflattenedRows;
|
||||
}, [fields]);
|
||||
|
||||
const validateForm = useCallback(() => {
|
||||
@@ -283,12 +283,12 @@ const Form = (props) => {
|
||||
getField,
|
||||
processing,
|
||||
submitted,
|
||||
countRows,
|
||||
getDataByPath,
|
||||
getData,
|
||||
getSiblingData,
|
||||
validateForm,
|
||||
modified,
|
||||
setModified,
|
||||
getSiblingData,
|
||||
}}
|
||||
>
|
||||
<HiddenInput
|
||||
|
||||
@@ -8,6 +8,7 @@ import Button from '../../../elements/Button';
|
||||
import DraggableSection from '../../DraggableSection';
|
||||
import reducer from './reducer';
|
||||
import { useRenderedFields } from '../../RenderFields';
|
||||
import useForm from '../../Form/useForm';
|
||||
import useFieldType from '../../useFieldType';
|
||||
import Error from '../../Error';
|
||||
import { repeater } from '../../../../../validation/validations';
|
||||
@@ -35,6 +36,7 @@ const Repeater = (props) => {
|
||||
const dataToInitialize = initialData || defaultValue;
|
||||
const [rows, dispatchRows] = useReducer(reducer, []);
|
||||
const { customComponentsPath } = useRenderedFields();
|
||||
const { getDataByPath } = useForm();
|
||||
|
||||
const path = pathFromProps || name;
|
||||
|
||||
@@ -58,25 +60,32 @@ const Repeater = (props) => {
|
||||
});
|
||||
|
||||
const addRow = (rowIndex) => {
|
||||
const data = getDataByPath(path)?.[name];
|
||||
|
||||
dispatchRows({
|
||||
type: 'ADD', index: rowIndex,
|
||||
type: 'ADD', index: rowIndex, data,
|
||||
});
|
||||
|
||||
setValue(value + 1);
|
||||
};
|
||||
|
||||
const removeRow = (rowIndex) => {
|
||||
const data = getDataByPath(path)?.[name];
|
||||
|
||||
dispatchRows({
|
||||
type: 'REMOVE',
|
||||
index: rowIndex,
|
||||
data,
|
||||
});
|
||||
|
||||
setValue(value - 1);
|
||||
};
|
||||
|
||||
const moveRow = (moveFromIndex, moveToIndex) => {
|
||||
const data = getDataByPath(path)?.[name];
|
||||
|
||||
dispatchRows({
|
||||
type: 'MOVE', index: moveFromIndex, moveToIndex,
|
||||
type: 'MOVE', index: moveFromIndex, moveToIndex, data,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -90,7 +99,7 @@ const Repeater = (props) => {
|
||||
useEffect(() => {
|
||||
dispatchRows({
|
||||
type: 'SET_ALL',
|
||||
payload: dataToInitialize.reduce((acc, data) => ([
|
||||
rows: dataToInitialize.reduce((acc, data) => ([
|
||||
...acc,
|
||||
{
|
||||
key: uuidv4(),
|
||||
|
||||
@@ -2,16 +2,14 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const reducer = (currentState, action) => {
|
||||
const {
|
||||
type, index, moveToIndex, payload, data,
|
||||
type, index, moveToIndex, rows, data,
|
||||
} = action;
|
||||
|
||||
const stateCopy = [...currentState];
|
||||
|
||||
const movingRowState = stateCopy[index];
|
||||
|
||||
switch (type) {
|
||||
case 'SET_ALL':
|
||||
return payload;
|
||||
return rows;
|
||||
|
||||
case 'ADD':
|
||||
stateCopy.splice(index + 1, 0, {
|
||||
@@ -20,20 +18,40 @@ const reducer = (currentState, action) => {
|
||||
data,
|
||||
});
|
||||
|
||||
return stateCopy;
|
||||
data.splice(index + 1, 0, {});
|
||||
|
||||
return stateCopy.map((row, i) => {
|
||||
return {
|
||||
...row,
|
||||
data: {
|
||||
...(data[i] || {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
case 'REMOVE':
|
||||
stateCopy.splice(index, 1);
|
||||
return stateCopy;
|
||||
|
||||
case 'UPDATE_COLLAPSIBLE_STATUS':
|
||||
stateCopy[index].open = !movingRowState.open;
|
||||
stateCopy[index].open = !stateCopy[index].open;
|
||||
return stateCopy;
|
||||
|
||||
case 'MOVE':
|
||||
stateCopy.splice(index, 1);
|
||||
stateCopy.splice(moveToIndex, 0, movingRowState);
|
||||
return stateCopy;
|
||||
case 'MOVE': {
|
||||
const stateCopyWithNewData = stateCopy.map((row, i) => {
|
||||
return {
|
||||
...row,
|
||||
data: {
|
||||
...(data[i] || {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const movingRowState = { ...stateCopyWithNewData[index] };
|
||||
stateCopyWithNewData.splice(index, 1);
|
||||
stateCopyWithNewData.splice(moveToIndex, 0, movingRowState);
|
||||
return stateCopyWithNewData;
|
||||
}
|
||||
|
||||
default:
|
||||
return currentState;
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
useContext, useCallback, useEffect, useState,
|
||||
} from 'react';
|
||||
import FormContext from '../Form/Context';
|
||||
import { useLocale } from '../../utilities/Locale';
|
||||
import useDebounce from '../../../hooks/useDebounce';
|
||||
|
||||
import './index.scss';
|
||||
@@ -10,7 +9,6 @@ import './index.scss';
|
||||
const useFieldType = (options) => {
|
||||
const {
|
||||
path,
|
||||
required,
|
||||
initialData: data,
|
||||
defaultValue,
|
||||
validate,
|
||||
@@ -24,16 +22,11 @@ const useFieldType = (options) => {
|
||||
// If no initialData, use default value
|
||||
const initialData = data !== undefined ? data : defaultValue;
|
||||
|
||||
const locale = useLocale();
|
||||
const formContext = useContext(FormContext);
|
||||
const {
|
||||
dispatchFields, submitted, processing, getField, setModified, modified,
|
||||
} = formContext;
|
||||
|
||||
// Maintain an internal initial value AND value to ensure that the form
|
||||
// can successfully differentiate between updates sent from fields
|
||||
// that are meant to be initial values vs. values that are deliberately changed
|
||||
const [internalInitialValue, setInternalInitialValue] = useState(initialData);
|
||||
const [internalValue, setInternalValue] = useState(initialData);
|
||||
|
||||
// Debounce internal values to update form state only every 60ms
|
||||
@@ -45,7 +38,6 @@ const useFieldType = (options) => {
|
||||
|
||||
// Valid could be a string equal to an error message
|
||||
const valid = (field && typeof field.valid === 'boolean') ? field.valid : true;
|
||||
const valueFromForm = field?.value;
|
||||
const showError = valid === false && submitted;
|
||||
|
||||
// Method to send update field values from field component(s)
|
||||
@@ -79,38 +71,12 @@ const useFieldType = (options) => {
|
||||
setInternalValue(value);
|
||||
}, [setModified, modified]);
|
||||
|
||||
const setInitialValue = useCallback((value) => {
|
||||
setInternalInitialValue(value);
|
||||
}, []);
|
||||
|
||||
// Remove field from state on "unmount"
|
||||
// This is mostly used for repeater / flex content row modifications
|
||||
useEffect(() => {
|
||||
return () => dispatchFields({ path, type: 'REMOVE' });
|
||||
}, [dispatchFields, path]);
|
||||
|
||||
// Whenever the value from form updates,
|
||||
// update internal value as well
|
||||
useEffect(() => {
|
||||
if (valueFromForm !== undefined) {
|
||||
setInternalInitialValue(valueFromForm);
|
||||
}
|
||||
}, [valueFromForm, setInternalInitialValue]);
|
||||
|
||||
// When locale changes, and / or initialData changes,
|
||||
// reset internal initial value
|
||||
useEffect(() => {
|
||||
if (initialData !== undefined) {
|
||||
setInternalInitialValue(initialData);
|
||||
}
|
||||
}, [initialData, setInternalInitialValue, locale]);
|
||||
|
||||
// When internal initial value changes, set internal value
|
||||
// This is necessary to bypass changing the form to a modified:true state
|
||||
useEffect(() => {
|
||||
setInternalValue(internalInitialValue);
|
||||
}, [setInternalValue, internalInitialValue]);
|
||||
|
||||
// The only time that the FORM value should be updated
|
||||
// is when the debounced value updates. So, when the debounced value updates,
|
||||
// send it up to the form
|
||||
@@ -132,7 +98,6 @@ const useFieldType = (options) => {
|
||||
formSubmitted: submitted,
|
||||
formProcessing: processing,
|
||||
setValue,
|
||||
setInitialValue,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user