I've been going on for hours trying for stuff that I think would work but couldn't get it right. Can you help me solve this?
I have here my file browser context wherein I want to pass the folderData and call it to another function.
import { createContext, useContext } from 'react';
export interface fileBrowserContext {
folderData: string;
}
export const FileBrowserContext = createContext<fileBrowserContext>({
folderData: '',
});
export const useFileBrowserContext = () => useContext(FileBrowserContext);
And this is the component where I'm using it. I'm also using react-hook-form which is working when I click the submit button.
const FileBrowser = () => {
const { control, watch, handleSubmit, register, reset, errors } = useForm({
reValidateMode: 'onChange',
defaultValues: {
createFolder: '',
},
});
const [folderData, setFolderData] = useState<string>('');
async function onSubmit(formData: any) {
if (Object.keys(errors).length === 0) {
/* Create folder */
setFolderData(formData.createFolder);
reset({ createFolder: '' });
}
handleToastSuccess({
message: 'Folder Created',
});
}
function renderForm() {
return (
<Form onSubmit={handleSubmit(onSubmit)} className="CreateFolderForm">
<Form.Group>
<Form.Label>Create Folder</Form.Label>
<Form.Control
id="createFolder"
name="createFolder"
ref={register({
required: {
value: true,
message: 'This field is required.',
},
})}
/>
</Form.Group>
<Button
variant="contained"
color="primary"
type="submit"
>
Create
</Button>
</Form>
);
}
const {
isCreateFolderOpen,
setIsCreateFolderOpen,
handleFileCallback,
} = useFileActionHandler(
createFolder,
);
return (
<FileBrowserContext.Provider value={{ folderData: folderData }}>
<>
<h1> File Browser </h1>
{folderData + ','}
</>
<CustomModal
showModal={isCreateFolderOpen}
setShowModal={setIsCreateFolderOpen}
modalHeading="Create Folder"
modalContent={renderForm()}
/>
</FileBrowserContext.Provider>
)
}
And I'm calling my useFileBrowserContext() on my utils.tsx. I need the folderData from the component above in which I'm planning to call using context to be able to save the folder in the Chonky library that I'm using.
export const useFileActionHandler = (
setCurrentFolderId: (folderId: string) => void,
createFolder: (folderName: string) => void,
) => {
const [isCreateFolderOpen, setIsCreateFolderOpen] = useState<boolean>(false);
const { folderData } = useFileBrowserContext();
const handleFileCallback = useCallback(
(data: ChonkyFileActionData) => {
if (data.id === ChonkyActions.CreateFolder.id) {
// CONTEXT BEING CALLED HERE
setIsCreateFolderOpen(true);
if (folderData) createFolder(folderData);
}
console.log('file browser', folderData);
},
[
createFolder,
],
);
return {
handleFileCallback,
isCreateFolderOpen,
setIsCreateFolderOpen,
};
};
There is no error in my console but the behavior is that it when I console log the folderData in utils.tsx, it returns an empty string which I think means that the folderData being passed from the component is not working. Thanks in advance for any help.
In order for components to use useContext
, they must be the descendent of a Context.Provider
. In your case, you're trying to use your FileBrowserContext
within your FileBrowser
component, but it's not a descendent of a Context.Provider
, so it receives the default value for folderData
(the empty string).
So, currently you have:
<App>
<FileBrowser>
<FileBrowserContext.Provider>
{/* ... */}
</FileBrowserContext.Provider>
</FileBrowser>
</App>
but you need
<App>
<FileBrowserContext.Provider>
<FileBrowser>
{/* ... */}
</FileBrowser>
</FileBrowserContext.Provider>
</App>
The fix is to move your Context.Provider
component up a level and render it as a parent of FileBrowser
, instead of having FileBrowser
itself render it. You can wrap it into a custom component that handles setting the correct value
like so:
const FileBrowserContextProvider = () => {
const [folderData, setFolderData] = useState('');
return (
<FileBrowserContext.Provider value={{ folderData, setFolderData }}>
{children}
</FileBrowserContext.Provider>
);
};
const App = () => {
return (
<FileBrowserContextProvider>
<FileBrowser /> {/* <-- now FileBrowser can safely useContext
since it's a descendent of the Provider */}
</FileBrowserContextProvider>
);
};