react-ketting
Version:
Ketting bindings for React
141 lines • 5.46 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.useNewResource = void 0;
const ketting_1 = require("ketting");
const react_1 = require("react");
const use_read_resource_1 = require("./use-read-resource");
const use_resolve_resource_1 = require("./use-resolve-resource");
const use_client_1 = require("./use-client");
/**
* useNewResource is a hook that helps you create new resources on a typical
* REST api.
*
* This hook is for a specific use-case in React application; use this hook
* if you want to present the user a form to create the resource, and after
* creation the user *stays* on that form and continues editing after
* creation.
*
* If you're just looking for a way to do a POST request and redirect/refresh
* the interface, don't use this hook. Instead, you probably just want to call
* `someResource.postFollow()` in an event handler and do something specific
* after it was successful.
*
* A general guideline of when this component is useful is if 'creating'
* and 'editing' is continuous and not really discernable from a UX
* perspective.
*
* The assumptions this hooks makes:
*
* 1. You create new resources with POST requests.
* 2. The body of the POST request is the same (or similar enough) to the
* body of the resource you're eventually creating.
* 3. The server returns a 201 Created status code when successful.
* 4. The server also returns a Location header referring to the new resource.
* 5. After creation, any changes the user makes result in a `PUT` request to
* the new location.
*
* Example call:
*
* <pre>
* const {
* error,
* resourceState,
* setResourceState,
* submit
* } = useNewResource(targetResource, { initialData: { foo: bar });
* </pre>
*
* You must pass some 'initial data' to this function that's used to
* initialize the data object. Think of this as the 'template' or starting
* value.
*
* Instead of 'initialData', you may also pass 'initialState', which requires
* a fully fledged 'State' object.
*
*
* Returned properties:
*
* * error - Will be null or an error object.
* * resourceState - A state object. The `.data` property of this object will
* contain the parsed JSON from the server.
* * setResourceState - Update the local cache of the resource.
* * submit - When called the first time, sends a POST request to the
* collection/target resource. The second time it will send a PUT
* request to the previously created resource.
* </pre>
*
* To do POST requests you must specifiy initialState with the state the user starts
* off with.
*/
function useNewResource(targetResource, options) {
const client = (0, use_client_1.useClient)();
const [resource, setResource] = (0, react_1.useState)(() => createSyntheticResource(client, options));
const { resource: realTargetResource } = (0, use_resolve_resource_1.useResolveResource)(targetResource);
const useResourceResult = (0, use_read_resource_1.useReadResource)(resource, {
refreshOnStale: options === null || options === void 0 ? void 0 : options.refreshOnStale
});
return {
loading: resource !== null,
error: useResourceResult.error,
resourceState: useResourceResult.resourceState,
setResourceState: newResourceState => {
resource.updateCache(newResourceState);
},
submit: async () => {
if (resource.uri.startsWith('urn:uuid:')) {
// Creating a new resource. Yay!
if (!realTargetResource) {
throw new Error('Tried to create a new resource on a collection that wasn\'t fully loaded');
}
const newResource = await realTargetResource.postFollow(useResourceResult.resourceState);
setResource(newResource);
}
else {
// Updating an earlier resource
resource.put(useResourceResult.resourceState);
}
},
data: useResourceResult.resourceState.data,
setData: (data) => {
useResourceResult.resourceState.data = data;
resource.updateCache(useResourceResult.resourceState);
},
resource,
};
}
exports.useNewResource = useNewResource;
function createSyntheticResource(client, options) {
// Create a unique fake URI.
const uri = `urn:uuid:${uuidv4()}`;
// Open this URI. Ketting does not do HTTP requests unless asked, so this is
// safe.
const resource = client.go(uri);
let resourceState;
// Build the 'resourceState'
if ('initialData' in options) {
resourceState = new ketting_1.HalState({
uri,
client,
headers: new Headers(),
data: options.initialData,
links: new ketting_1.Links(uri),
});
}
else {
resourceState = options.initialState.clone();
// Fix the uri or else it might get weird
resourceState.uri = uri;
}
// Prime the cache
resource.updateCache(resourceState);
return resource;
}
/**
* Taken from https://stackoverflow.com/a/68141099/80911
*/
function uuidv4() {
const a = crypto.getRandomValues(new Uint16Array(8));
let i = 0;
return '00-0-4-1-000'.replace(/[^-]/g, (s) => (a[i++] + (+s) * 0x10000 >> +(s)).toString(16).padStart(4, '0'));
}
//# sourceMappingURL=use-new-resource.js.map
;