encapsulates button into context

This commit is contained in:
Benson Wally Tran 2023-11-18 19:53:37 -06:00
parent 2fc30b9603
commit 783e7969db
4 changed files with 84 additions and 32 deletions

View File

@ -1,33 +1,13 @@
'use-client'; 'use-client';
import chatOperations from 'operations/chatOperations';
import { useState } from 'react';
import GenerateStoryContextProvider, { IGenerateStoryContext } from './GenerateStoryContext'; import GenerateStoryContextProvider, { IGenerateStoryContext } from './GenerateStoryContext';
export default function GenerateStoryComponent() { export default function GenerateStoryComponent() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState();
const getStory = async () => {
setLoading(true);
const data = await chatOperations.createStoryAsync();
setData(data);
setLoading(false);
};
return ( return (
<GenerateStoryContextProvider> <GenerateStoryContextProvider>
{({ story }: IGenerateStoryContext) => { {({ story, loading }: IGenerateStoryContext) => {
return ( return (
<main className="flex min-h-screen flex-col items-center justify-between p-24"> <main className="flex min-h-screen flex-col items-center justify-between p-24">
<button
onClick={getStory}
className="mb-10 rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
>
Run A New Story
</button>
{loading ? <div className="mb10">Loading...</div> : null} {loading ? <div className="mb10">Loading...</div> : null}
{JSON.stringify(data)}
</main> </main>
); );
}} }}

View File

@ -1,23 +1,22 @@
'use client'; 'use client';
import { IStory } from 'operations/chatOperations'; import chatOperations, { IStory } from 'operations/chatOperations';
import { PropsWithChildren, createContext, useContext, useMemo, useState } from 'react'; import { PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';
export interface IGenerateStoryContext { export interface IGenerateStoryContext {
story?: IStory; story?: IStory;
setStory: (story: IStory) => void;
images: string[]; images: string[];
setImages: (images: string[]) => void; loading: boolean;
} }
const GenerateStoryContext = createContext<IGenerateStoryContext>({ const GenerateStoryContext = createContext<IGenerateStoryContext>({
story: undefined, story: undefined,
setStory: () => {},
images: [], images: [],
setImages: () => {} loading: false
}); });
function GenerateStoryContextProvider({ children }: { children: PropsWithChildren<any> }) { function GenerateStoryContextProvider({ children }: { children: PropsWithChildren<any> }) {
const [loading, setLoading] = useState<boolean>(false);
const [story, setStory] = useState<IStory>(); const [story, setStory] = useState<IStory>();
/* /*
Note(Benson): For now images is an array of urls where each index in the array Note(Benson): For now images is an array of urls where each index in the array
@ -27,13 +26,26 @@ function GenerateStoryContextProvider({ children }: { children: PropsWithChildre
const [images, setImages] = useState<string[]>([]); const [images, setImages] = useState<string[]>([]);
const value = useMemo<IGenerateStoryContext>( const value = useMemo<IGenerateStoryContext>(
() => ({ story, setStory, images, setImages }), () => ({ story, images, loading }),
[story, setStory, images, setImages] [story, images, loading]
); );
return ( return (
<GenerateStoryContext.Provider value={value}> <GenerateStoryContext.Provider value={value}>
<>
<button
onClick={async () => {
setLoading(true);
const story = await chatOperations.createStoryAsync();
setStory(story);
setLoading(false);
}}
className="mb-10 rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
>
Run A New Story
</button>
{typeof children === 'function' ? children(value) : children} {typeof children === 'function' ? children(value) : children}
</>
</GenerateStoryContext.Provider> </GenerateStoryContext.Provider>
); );
} }

View File

@ -0,0 +1,56 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import dependenciesMatch from 'utils/dependenciesMatch';
const usePromiseMemo = <T, E = unknown>(
promise: () => Promise<T>,
nextDeps: unknown[]
): { results?: T; error?: E; loading: boolean; refetch: () => void } => {
const [results, setResults] = useState<T>();
const [error, setError] = useState<E>();
const [hasFinished, setHasFinished] = useState<boolean>(false);
const dependencies = useRef<unknown[]>(nextDeps);
const isMounted = useRef(true);
const checkIfPromiseIsStillValid = useCallback(
(dependenciesAtTimeOfPromise: unknown[]): boolean => {
return (
isMounted.current && dependenciesMatch(dependenciesAtTimeOfPromise, dependencies.current)
);
},
[]
);
const run = useCallback(() => {
setHasFinished(false);
promise()
.then((r) => checkIfPromiseIsStillValid(nextDeps) && setResults(r))
.catch((e) => checkIfPromiseIsStillValid(nextDeps) && setError(e))
.finally(() => checkIfPromiseIsStillValid(nextDeps) && setHasFinished(true));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, nextDeps);
useEffect(() => {
isMounted.current = true;
dependencies.current = nextDeps;
run();
return () => {
isMounted.current = false;
};
// nextDeps is already a dependency of run
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [run]);
return useMemo(
() => ({
results,
error,
loading: !hasFinished,
refetch: run
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[hasFinished, results]
);
};
export default usePromiseMemo;

View File

@ -40,9 +40,13 @@ async function createStoryAsync(
content: userPrompt content: userPrompt
} }
]; ];
try {
const data = await post('/api/open-ai/chat', generateRequestPayload(messages)); const data = await post('/api/open-ai/chat', generateRequestPayload(messages));
// const data = await post('/api/revalidate', generateRequestPayload(messages));
return getFunctionCallArguments<IStory>(data); return getFunctionCallArguments<IStory>(data);
} catch (e) {
console.error(e);
throw e;
}
} }
export default { createStoryAsync }; export default { createStoryAsync };