useCallback with async is ok, after all (#4934)

This commit is contained in:
Dimitri Mitropoulos 2022-07-07 11:04:41 -04:00 committed by GitHub
parent 53fbc151ef
commit f72c5a6969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 172 additions and 205 deletions

View File

@ -17,18 +17,15 @@ export const CopyButton: FC<Props> = ({
...buttonProps
}) => {
const [showConfirmation, setshowConfirmation] = useState(false);
const onClick = useCallback((event: React.MouseEvent) => {
const fn = async () => {
event.preventDefault();
event.stopPropagation();
const toCopy = typeof content === 'string' ? content : await content();
const onClick = useCallback(async (event: React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
const toCopy = typeof content === 'string' ? content : await content();
if (toCopy) {
clipboard.writeText(toCopy);
}
setshowConfirmation(true);
};
fn();
if (toCopy) {
clipboard.writeText(toCopy);
}
setshowConfirmation(true);
}, [content]);
useInterval(() => {

View File

@ -45,7 +45,7 @@ interface Props {
export const AuthDropdown: FC<Props> = ({ onChange }) => {
const activeRequest = useSelector(selectActiveRequest);
const onClick = useCallback((type: string) => {
const onClick = useCallback(async (type: string) => {
if (!activeRequest) {
return;
}
@ -57,37 +57,34 @@ export const AuthDropdown: FC<Props> = ({ onChange }) => {
const { authentication } = activeRequest;
const fn = async () => {
if (type === authentication.type) {
if (type === authentication.type) {
// Type didn't change
return;
return;
}
const newAuthentication = models.request.newAuth(type, authentication);
const defaultAuthentication = models.request.newAuth(authentication.type);
// Prompt the user if fields will change between new and old
for (const key of Object.keys(authentication)) {
if (key === 'type') {
continue;
}
const newAuthentication = models.request.newAuth(type, authentication);
const defaultAuthentication = models.request.newAuth(authentication.type);
const value = authentication[key];
const changedSinceDefault = defaultAuthentication[key] !== value;
const willChange = newAuthentication[key] !== value;
// Prompt the user if fields will change between new and old
for (const key of Object.keys(authentication)) {
if (key === 'type') {
continue;
}
const value = authentication[key];
const changedSinceDefault = defaultAuthentication[key] !== value;
const willChange = newAuthentication[key] !== value;
if (changedSinceDefault && willChange) {
await showModal(AlertModal, {
title: 'Switch Authentication?',
message: 'Current authentication settings will be lost',
addCancel: true,
});
break;
}
if (changedSinceDefault && willChange) {
await showModal(AlertModal, {
title: 'Switch Authentication?',
message: 'Current authentication settings will be lost',
addCancel: true,
});
break;
}
onChange(activeRequest, newAuthentication);
};
fn();
}
onChange(activeRequest, newAuthentication);
}, [onChange, activeRequest]);
const isCurrent = useCallback((type: string) => {

View File

@ -53,42 +53,36 @@ export const RequestActionsDropdown = forwardRef<Dropdown, Props>(({
const [actionPlugins, setActionPlugins] = useState<RequestAction[]>([]);
const [loadingActions, setLoadingActions] = useState<Record<string, boolean>>({});
const onOpen = useCallback(() => {
const fn = async () => {
const actionPlugins = await getRequestActions();
setActionPlugins(actionPlugins);
};
fn();
const onOpen = useCallback(async () => {
const actionPlugins = await getRequestActions();
setActionPlugins(actionPlugins);
}, []);
const handlePluginClick = useCallback(({ plugin, action, label }: RequestAction) => {
const fn = async () => {
setLoadingActions({ ...loadingActions, [label]: true });
const handlePluginClick = useCallback(async ({ plugin, action, label }: RequestAction) => {
setLoadingActions({ ...loadingActions, [label]: true });
try {
const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null;
const context = {
...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER)),
...pluginContexts.data.init(activeProject._id),
...(pluginContexts.store.init(plugin)),
...(pluginContexts.network.init(activeEnvironmentId)),
};
await action(context, {
request,
requestGroup,
});
} catch (error) {
showError({
title: 'Plugin Action Failed',
error,
});
}
setLoadingActions({ ...loadingActions, [label]: false });
if (ref && 'current' in ref) { // this `in` operator statement type-narrows to `MutableRefObject`
ref.current?.hide();
}
};
fn();
try {
const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null;
const context = {
...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER)),
...pluginContexts.data.init(activeProject._id),
...(pluginContexts.store.init(plugin)),
...(pluginContexts.network.init(activeEnvironmentId)),
};
await action(context, {
request,
requestGroup,
});
} catch (error) {
showError({
title: 'Plugin Action Failed',
error,
});
}
setLoadingActions({ ...loadingActions, [label]: false });
if (ref && 'current' in ref) { // this `in` operator statement type-narrows to `MutableRefObject`
ref.current?.hide();
}
}, [request, activeEnvironment, requestGroup, loadingActions, activeProject._id, ref]);
const duplicate = useCallback(() => {

View File

@ -61,10 +61,9 @@ export const RequestGroupActionsDropdown = forwardRef<RequestGroupActionsDropdow
},
}));
const onOpen = useCallback(() => {
getRequestGroupActions().then(actionPlugins => {
setActionPlugins(actionPlugins);
});
const onOpen = useCallback(async () => {
const actionPlugins = await getRequestGroupActions();
setActionPlugins(actionPlugins);
}, []);
const handleRequestGroupDuplicate = useCallback(() => {
@ -75,49 +74,46 @@ export const RequestGroupActionsDropdown = forwardRef<RequestGroupActionsDropdow
createRequestGroup(requestGroup._id);
}, [requestGroup._id]);
const handleDeleteFolder = useCallback(() => {
models.stats.incrementDeletedRequestsForDescendents(requestGroup).then(() => {
models.requestGroup.remove(requestGroup);
});
const handleDeleteFolder = useCallback(async () => {
await models.stats.incrementDeletedRequestsForDescendents(requestGroup);
models.requestGroup.remove(requestGroup);
}, [requestGroup]);
const handleEditEnvironment = useCallback(() => {
showModal(EnvironmentEditModal, requestGroup);
}, [requestGroup]);
const handlePluginClick = useCallback(({ label, plugin, action }: RequestGroupAction) => {
const fn = async () => {
setLoadingActions({ ...loadingActions, [label]: true });
const handlePluginClick = useCallback(async ({ label, plugin, action }: RequestGroupAction) => {
setLoadingActions({ ...loadingActions, [label]: true });
try {
const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null;
const context = {
...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER) as Record<string, any>),
...pluginContexts.data.init(activeProject._id),
...(pluginContexts.store.init(plugin) as Record<string, any>),
...(pluginContexts.network.init(activeEnvironmentId) as Record<string, any>),
};
const requests = await models.request.findByParentId(requestGroup._id);
requests.sort((a, b) => a.metaSortKey - b.metaSortKey);
await action(context, {
requestGroup,
requests,
});
} catch (err) {
showError({
title: 'Plugin Action Failed',
error: err,
});
}
setLoadingActions({
...loadingActions,
[label]: false,
try {
const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null;
const context = {
...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER) as Record<string, any>),
...pluginContexts.data.init(activeProject._id),
...(pluginContexts.store.init(plugin) as Record<string, any>),
...(pluginContexts.network.init(activeEnvironmentId) as Record<string, any>),
};
const requests = await models.request.findByParentId(requestGroup._id);
requests.sort((a, b) => a.metaSortKey - b.metaSortKey);
await action(context, {
requestGroup,
requests,
});
} catch (err) {
showError({
title: 'Plugin Action Failed',
error: err,
});
}
setLoadingActions({
...loadingActions,
[label]: false,
});
dropdownRef.current?.hide();
dropdownRef.current?.hide();
};
fn();
}, [dropdownRef, loadingActions, activeEnvironment, requestGroup, activeProject]);
return (

View File

@ -121,7 +121,9 @@ export const ResponseHistoryDropdown: FC<Props> = ({
);
};
const handleKeydown = useCallback((event: KeyboardEvent) => executeHotKey(event, hotKeyRefs.REQUEST_TOGGLE_HISTORY, () => dropdownRef.current?.toggle(true)), []);
const handleKeydown = useCallback((event: KeyboardEvent) => {
executeHotKey(event, hotKeyRefs.REQUEST_TOGGLE_HISTORY, () => dropdownRef.current?.toggle(true));
}, []);
const environmentName = activeEnvironment ? activeEnvironment.name : 'Base';
const isLatestResponseActive = !responses.length || activeResponse._id === responses[0]._id;
return (

View File

@ -92,15 +92,12 @@ const useDesignEmptyState = () => {
}
}, [contents, shouldIncrementCounter]);
const onUpdateContents = useCallback((value: string) => {
const onUpdateContents = useCallback(async (value: string) => {
if (!activeApiSpec) {
return;
}
const fn = async () => {
await models.apiSpec.update({ ...activeApiSpec, contents: value });
};
fn();
await models.apiSpec.update({ ...activeApiSpec, contents: value });
// Because we can't await activeApiSpec.contents to have propageted to redux, we flip a toggle to decide if we should do something when redux does eventually change
setShouldIncrementCounter(true);
@ -124,15 +121,12 @@ const RenderEditor: FC<{ editor: RefObject<UnconnectedCodeEditor> }> = ({ editor
const { uniquenessKey, emptyStateNode } = useDesignEmptyState();
const onCodeEditorChange = useMemo(() => {
const handler = (contents: string) => {
const fn = async () => {
if (!activeApiSpec) {
return;
}
const handler = async (contents: string) => {
if (!activeApiSpec) {
return;
}
await models.apiSpec.update({ ...activeApiSpec, contents });
};
fn();
await models.apiSpec.update({ ...activeApiSpec, contents });
};
return debounce(handler, 500);

View File

@ -104,41 +104,35 @@ const WrapperUnitTest: FC<Props> = ({
const selectableRequests = buildSelectableRequests();
const handleSetActiveUnitTestSuite = useCallback((unitTestSuite: UnitTestSuite) => {
const fn = async () => {
if (!activeWorkspace) {
return;
}
const handleSetActiveUnitTestSuite = useCallback(async (unitTestSuite: UnitTestSuite) => {
if (!activeWorkspace) {
return;
}
await models.workspaceMeta.updateByParentId(activeWorkspace._id, {
activeUnitTestSuiteId: unitTestSuite._id,
});
};
fn();
await models.workspaceMeta.updateByParentId(activeWorkspace._id, {
activeUnitTestSuiteId: unitTestSuite._id,
});
}, [activeWorkspace]);
const handleCreateTestSuite = useCallback(() => {
const fn = async () => {
if (!activeWorkspace) {
return;
}
showPrompt({
title: 'New Test Suite',
defaultValue: 'New Suite',
submitName: 'Create Suite',
label: 'Test Suite Name',
selectText: true,
onComplete: async name => {
const unitTestSuite = await models.unitTestSuite.create({
parentId: activeWorkspace._id,
name,
});
await handleSetActiveUnitTestSuite(unitTestSuite);
trackSegmentEvent(SegmentEvent.testSuiteCreate);
},
});
};
fn();
const handleCreateTestSuite = useCallback(async () => {
if (!activeWorkspace) {
return;
}
showPrompt({
title: 'New Test Suite',
defaultValue: 'New Suite',
submitName: 'Create Suite',
label: 'Test Suite Name',
selectText: true,
onComplete: async name => {
const unitTestSuite = await models.unitTestSuite.create({
parentId: activeWorkspace._id,
name,
});
await handleSetActiveUnitTestSuite(unitTestSuite);
trackSegmentEvent(SegmentEvent.testSuiteCreate);
},
});
}, [handleSetActiveUnitTestSuite, activeWorkspace]);
const generateSendReqSnippet = useCallback((existingCode: string, requestId: string) => {
@ -240,22 +234,20 @@ const WrapperUnitTest: FC<Props> = ({
models.unitTest.update(unitTest, { requestId });
}, []);
const handleDeleteUnitTestSuite = useCallback((unitTestSuite: UnitTestSuite) => {
return () => {
showAlert({
title: `Delete ${unitTestSuite.name}`,
message: (
<span>
Really delete <strong>{unitTestSuite.name}</strong>?
</span>
),
addCancel: true,
onConfirm: async () => {
await models.unitTestSuite.remove(unitTestSuite);
trackSegmentEvent(SegmentEvent.testSuiteDelete);
},
});
};
const handleDeleteUnitTestSuite = useCallback((unitTestSuite: UnitTestSuite) => () => {
showAlert({
title: `Delete ${unitTestSuite.name}`,
message: (
<span>
Really delete <strong>{unitTestSuite.name}</strong>?
</span>
),
addCancel: true,
onConfirm: async () => {
await models.unitTestSuite.remove(unitTestSuite);
trackSegmentEvent(SegmentEvent.testSuiteDelete);
},
});
}, []);
const handleChangeTestName = useCallback((unitTest: UnitTest, name?: string) => {
@ -270,47 +262,42 @@ const WrapperUnitTest: FC<Props> = ({
models.unitTestSuite.update(activeUnitTestSuite, { name });
}, [activeUnitTestSuite]);
const _runTests = useCallback((unitTests: UnitTest[]) => {
const fn = async () => {
if (!activeWorkspace) {
return;
}
setTestsRunning(unitTests);
setResultsError(null);
const tests: Test[] = unitTests.map(t => ({ name: t.name, code: t.code, defaultRequestId: t.requestId }));
const src = await generate([{ name: 'My Suite', suites: [], tests }]);
const sendRequest = getSendRequestCallback(activeEnvironment?._id);
const _runTests = useCallback(async (unitTests: UnitTest[]) => {
if (!activeWorkspace) {
return;
}
setTestsRunning(unitTests);
setResultsError(null);
const tests: Test[] = unitTests.map(t => ({ name: t.name, code: t.code, defaultRequestId: t.requestId }));
const src = generate([{ name: 'My Suite', suites: [], tests }]);
const sendRequest = getSendRequestCallback(activeEnvironment?._id);
try {
const results = await runTests(src, { sendRequest });
await models.unitTestResult.create({
results,
parentId: activeWorkspace._id,
});
try {
const results = await runTests(src, { sendRequest });
await models.unitTestResult.create({
results,
parentId: activeWorkspace._id,
});
setTestsRunning(null);
} catch (err) {
// Set the state after a timeout so the user still sees the loading state
setTimeout(() => {
setTestsRunning(null);
} catch (err) {
// Set the state after a timeout so the user still sees the loading state
setTimeout(() => {
setTestsRunning(null);
setResultsError(err.message);
}, 400);
return;
}
};
return fn();
setResultsError(err.message);
}, 400);
return;
}
}, [activeEnvironment?._id, activeWorkspace]);
const handleRunTests = useCallback(() => {
_runTests(activeUnitTests).then(() => {
trackSegmentEvent(SegmentEvent.unitTestRunAll);
});
const handleRunTests = useCallback(async () => {
await _runTests(activeUnitTests);
trackSegmentEvent(SegmentEvent.unitTestRunAll);
}, [_runTests, activeUnitTests]);
const handleRunTest = useCallback(async (unitTest: UnitTest) => {
_runTests([unitTest]).then(() => {
trackSegmentEvent(SegmentEvent.unitTestRun);
});
await _runTests([unitTest]);
trackSegmentEvent(SegmentEvent.unitTestRun);
}, [_runTests]);
return (