如何在 Typescript 中正确使用 React Context Provider?
How to properly use React Context Provider with Typescript?
我已经持续数小时尝试寻找我认为可行但无法正确完成的东西。你能帮我解决这个问题吗?
我这里有我的文件浏览器上下文,我想在其中传递 folderData 并将其调用到另一个函数。
import { createContext, useContext } from 'react';
export interface fileBrowserContext {
folderData: string;
}
export const FileBrowserContext = createContext<fileBrowserContext>({
folderData: '',
});
export const useFileBrowserContext = () => useContext(FileBrowserContext);
这是我正在使用的组件。我也在使用 react-hook-form,当我点击提交按钮时它正在工作。
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>
)
}
我正在 utils.tsx 上调用 useFileBrowserContext()。我需要来自上面组件的 folderData,我计划在其中调用使用上下文,以便能够将文件夹保存在我正在使用的 Chonky 库中。
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,
};
};
我的控制台没有错误,但行为是当我在 utils.tsx 中记录 folderData 时,它 returns 一个空字符串,我认为这意味着 folderData 从该组件不工作。在此先感谢您的帮助。
为了让组件使用 useContext
,它们必须是 Context.Provider
的 descendent。在您的情况下,您尝试在 FileBrowser
组件中使用 FileBrowserContext
,但它不是 Context.Provider
的后代,因此它接收 folderData
的默认值(空字符串)。
所以,目前你有:
<App>
<FileBrowser>
<FileBrowserContext.Provider>
{/* ... */}
</FileBrowserContext.Provider>
</FileBrowser>
</App>
但你需要
<App>
<FileBrowserContext.Provider>
<FileBrowser>
{/* ... */}
</FileBrowser>
</FileBrowserContext.Provider>
</App>
解决方法是将您的 Context.Provider
组件向上移动一个级别并将其呈现为 FileBrowser
的父级,而不是让 FileBrowser
本身呈现它。您可以将其包装到一个自定义组件中,该组件处理设置正确的 value
,如下所示:
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>
);
};
我已经持续数小时尝试寻找我认为可行但无法正确完成的东西。你能帮我解决这个问题吗?
我这里有我的文件浏览器上下文,我想在其中传递 folderData 并将其调用到另一个函数。
import { createContext, useContext } from 'react';
export interface fileBrowserContext {
folderData: string;
}
export const FileBrowserContext = createContext<fileBrowserContext>({
folderData: '',
});
export const useFileBrowserContext = () => useContext(FileBrowserContext);
这是我正在使用的组件。我也在使用 react-hook-form,当我点击提交按钮时它正在工作。
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>
)
}
我正在 utils.tsx 上调用 useFileBrowserContext()。我需要来自上面组件的 folderData,我计划在其中调用使用上下文,以便能够将文件夹保存在我正在使用的 Chonky 库中。
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,
};
};
我的控制台没有错误,但行为是当我在 utils.tsx 中记录 folderData 时,它 returns 一个空字符串,我认为这意味着 folderData 从该组件不工作。在此先感谢您的帮助。
为了让组件使用 useContext
,它们必须是 Context.Provider
的 descendent。在您的情况下,您尝试在 FileBrowser
组件中使用 FileBrowserContext
,但它不是 Context.Provider
的后代,因此它接收 folderData
的默认值(空字符串)。
所以,目前你有:
<App>
<FileBrowser>
<FileBrowserContext.Provider>
{/* ... */}
</FileBrowserContext.Provider>
</FileBrowser>
</App>
但你需要
<App>
<FileBrowserContext.Provider>
<FileBrowser>
{/* ... */}
</FileBrowser>
</FileBrowserContext.Provider>
</App>
解决方法是将您的 Context.Provider
组件向上移动一个级别并将其呈现为 FileBrowser
的父级,而不是让 FileBrowser
本身呈现它。您可以将其包装到一个自定义组件中,该组件处理设置正确的 value
,如下所示:
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>
);
};