React中Context内调用自定义hook或hook内用useContext的最佳实践
问题描述
我需要在多个组件间共享状态,因此决定使用自定义Context。目前在Context的ChatProvider中调用自定义hook useChat()获取状态变量,但不确定这是否为最佳实践,或是应该在useChat内部调用useContext?
原本所有状态都放在Context中,因代码过长可读性变差,我将其抽取到useChat hook中,但不确定该模式是否合理。当前我在各组件中调用useContext(ChatContext),是否应改为在useChat内部消费Context,再在组件中直接调用useChat?
另外,是否应该在useChat hook内部创建并管理Context?
现有代码
ChatProvider 代码
type ChatContextType = { ... } export const ChatContext = createContext<ChatContextType> ({ ... }); export const ChatProvider = ({ children }: any) => { const { chatId, message, chatTitle, ...rest } = useChat() // custom hook. is this best practice? return ( <ChatContext.Provider value={{ chatId, message, chatTitle, ...rest }}> {children} </ChatContext.Provider> ) }
useChat hook 代码
import { useState } from "react"; import { Chat, ChatMessage } from "@/components/ChatInput/classes"; import { nanoid } from "nanoid"; import { useRouter } from "next/router"; const useChat = () => { const [chatId, setChatId] = useState(nanoid()); const [message, setMessage] = useState<string>(""); const [chatTitle, setChatTitle] = useState<string>(""); const [chatMessages, setChatMessages] = useState<ChatMessage[]>([]); const router = useRouter(); const startNewChat = () => { setChatId(() => nanoid()); setChatTitle(() => ''); setChatMessages(() => [] as ChatMessage[]); }; return { chatId, setChatId, message, setMessage, chatTitle, setChatTitle, chatMessages, setChatMessages, startNewChat, } }; export default useChat;
解答
1. 在ChatProvider中调用useChat是合理且推荐的实践
你当前的做法完全没问题,这是拆分复杂状态逻辑的标准方式。把状态管理的细节(比如useState声明、业务方法startNewChat)封装到useChat里,能让ChatProvider的代码更简洁,专注于“把状态注入Context”这单一职责,同时提升整体代码的可读性和可维护性。
2. 推荐在useChat内部封装useContext,让组件直接调用useChat
当前组件直接调用useContext(ChatContext)的方式虽然能工作,但不够友好:组件需要依赖ChatContext的定义,还要处理Context默认值的类型问题。更优的做法是:
- 改造
useChat,让它内部调用useContext(ChatContext)获取共享状态 - 把状态和方法直接返回给组件,同时增加兜底判断(防止组件在Provider外调用hook报错)
改造后的useChat示例:
import { useContext } from "react"; import { ChatContext } from "./ChatProvider"; const useChat = () => { const context = useContext(ChatContext); if (!context) { throw new Error("useChat 必须在 ChatProvider 内部使用"); } return context; }; export default useChat;
这样组件只需要调用useChat()就能拿到所有需要的内容,不用关心Context的底层实现,也减少了重复代码。
3. 绝对不要在useChat内部创建Context
Context的创建必须是全局或模块级别的。如果在hook内部创建Context,每次调用useChat都会生成一个全新的Context实例,导致不同组件拿到的状态完全隔离,彻底失去了用Context共享状态的意义。所以Context必须在hook外部定义,由ChatProvider统一管理和注入。
内容的提问来源于stack exchange,提问作者Chris

