ra-core
Version:
Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React
88 lines (80 loc) • 3.2 kB
text/typescript
import { useEffect, useRef, useState } from 'react';
import { parse } from 'query-string';
import { Location, useLocation } from 'react-router-dom';
import isEqual from 'lodash/isEqual';
import { RaRecord } from '../types';
/**
* A hook that returns the record to use to override the values in a form
* @param options The hook options
* @param options.searchSource The key in the location search to use as a source for the record. Its content should be a stringified JSON object.
* @param options.stateSource The key in the location state to use as a source for the record
* @returns The record to use to override the values in a form
*/
export const useRecordFromLocation = (
props: UseRecordFromLocationOptions = {}
) => {
const { searchSource, stateSource } = props;
const location = useLocation();
const [recordFromLocation, setRecordFromLocation] = useState(() =>
getRecordFromLocation(location, {
stateSource,
searchSource,
})
);
// To avoid having the form resets when the location changes but the final record is the same
// This is needed for forms such as TabbedForm or WizardForm that may change the location for their sections
const previousRecordRef = useRef(recordFromLocation);
useEffect(() => {
const newRecordFromLocation = getRecordFromLocation(location, {
stateSource,
searchSource,
});
if (!isEqual(newRecordFromLocation, previousRecordRef.current)) {
previousRecordRef.current = newRecordFromLocation;
setRecordFromLocation(newRecordFromLocation);
}
}, [location, stateSource, searchSource]);
return recordFromLocation;
};
export type UseRecordFromLocationOptions = {
searchSource?: string;
stateSource?: string;
};
/**
* Get the initial record from the location, whether it comes from the location
* state or is serialized in the url search part.
*/
export const getRecordFromLocation = (
{ state, search }: Location,
{
searchSource = 'source',
stateSource = 'record',
}: {
searchSource?: string;
stateSource?: string;
} = {}
): Partial<RaRecord> | null => {
if (state && state[stateSource]) {
return state[stateSource];
}
if (search) {
try {
const searchParams = parse(search);
const source = searchParams[searchSource];
if (source) {
if (Array.isArray(source)) {
console.error(
`Failed to parse location ${searchSource} parameter '${search}'. To pre-fill some fields in the Create form, pass a stringified ${searchSource} parameter (e.g. '?${searchSource}={"title":"foo"}')`
);
return null;
}
return JSON.parse(source);
}
} catch (e) {
console.error(
`Failed to parse location ${searchSource} parameter '${search}'. To pre-fill some fields in the Create form, pass a stringified ${searchSource} parameter (e.g. '?${searchSource}={"title":"foo"}')`
);
}
}
return null;
};