import { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import debugLib from 'debug';
const debug = debugLib('SlimmingWorld:FormRouteSync');
const usePrevious = value => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
/**
* Utility component that syncs the form values of the form it is placed in
* to the route query string. When the fields given to the `fields` prop change value,
* the component calls the `syncValuesToRoute` action, which will update the route
* to match the values.
*
* Note: this util works one-way. It will not sync updates in the query back to the form
* values.
*/
const FormRouteSync = ({
values,
syncValuesToRoute,
transformValues,
fields,
pathname,
validateValues,
}) => {
const prevValues = usePrevious(values);
const prevpathname = usePrevious(pathname);
useEffect(() => {
if (values && prevValues && fields.some(key => values[key] !== prevValues[key])) {
if (pathname !== prevpathname) {
// prevent the route from updating while we are navigating to another page
debug('Route pathname changed since mount. Ignoring form updates');
return;
}
debug('Detected form value change. Updating route...');
if (transformValues) {
syncValuesToRoute(transformValues(values), fields);
} else if (validateValues) {
const filteredValues = Object.keys(values).reduce((filtered, key) => {
const value = values[key];
if (value > 0 || (typeof value === 'string' && value.length > 0)) {
// eslint-disable-next-line no-param-reassign
filtered[key] = value;
}
return filtered;
}, {});
syncValuesToRoute(filteredValues, fields);
} else {
syncValuesToRoute(values, fields);
}
}
});
return null;
};
FormRouteSync.propTypes = {
/**
* The names of the fields to sync to the query string
*/
fields: PropTypes.arrayOf(PropTypes.string).isRequired,
/**
* The current form values of the fields. Provided by the `connect()` redux wrapper
*/
values: PropTypes.objectOf(PropTypes.any),
/**
* The current route pathname. Used to block FormRouteSync if the pathname changes
*/
pathname: PropTypes.string,
/**
* An optional function to transform the values object before syncing it to the route.
* Should accept an object with values and return a new object with modifications.
*/
transformValues: PropTypes.func,
/**
* The action to sync values to route. Provided by the `connect()` redux wrapper
*/
syncValuesToRoute: PropTypes.func.isRequired,
};
export default FormRouteSync;