如何在 React 和 Typescript 中全局存储 ref
How to store a ref globally in React & Typescript
某个组件我有很多实例,我们称它为<Item>
。将鼠标悬停在任何 <Item>
上时,(quite 复杂且动态的)工具提示应出现在项目的位置。
因为我使用的是语义-ui-react,所以我选择的工具是使用Popup
来显示工具提示。使用弹出窗口的一种方法是给它们一个触发器组件(这将是我的 <Item>
)和一个内容组件,它会自动显示在触发器组件周围的特定位置。但是后来我的每个项目都有一个 Popup
实例,这显着增加了我的应用程序的响应时间,因为即使我目前不需要它们,也必须创建所有 Popup
s。
另一种使用 Popup
的方法是通过 context
属性 给它一个 ref,它将在那个 DOM 节点打开。因此,我想使用这种机制只有一个 Popup
,每当我将鼠标悬停在 <Item>
.[=35= 上时,它将接收对其 context
属性 的动态更新]
不过,为此,我需要将当前 <Item>
ref 传递给 Popup。因为我有一个深度嵌套的应用程序,所以我选择使用 https://github.com/diegohaz/constate 来全局共享状态。
不幸的是,共享 ref 似乎不起作用。每当我尝试从共享上下文中读取它时,它都是空的。
请看这里的简单示例:
https://codesandbox.io/s/pplw689627
虽然我可以在调试器中看到 tooltipTarget
写入 onMouseEnter
,但当它在 <Tooltip>
组件中读取时,它始终为 null。
知道我做错了什么吗?
我愿意接受不同的建议,但如果可能的话,我更愿意使用 Semantic 的 Popup 以保持一致的视觉风格。
更新:
Tom Finneys 的回答在上面的沙箱中有效,不幸的是我在我的实际环境中使用 Typescript 并且它不能立即工作。我在这个沙盒中得到了一个有效的解决方案:
https://codesandbox.io/s/7143r32klj
主要问题是 RefObject<T>
的 current
属性 是只读的。这可以防止您编写这样的上下文:
export function useTooltip() {
const [tooltipRecipe, setTooltipRecipe] = React.useState<string | undefined>(
undefined
);
let tooltipTarget = React.useRef<HTMLElement>(null);
const setTooltipTarget = (target: HTMLElement | null) => {
dummy.current = target;
};
return { tooltipRecipe, tooltipTarget, setTooltipRecipe, setTooltipTarget };
}
setTooltipTarget
无法编译。
我现在可以让它工作的唯一方法是将 RefObject<T>
强制转换为 current
不是只读的本地类型。这是 quite 一个黑客,但它有效。请参阅沙盒中的解决方案。
这里实际上正在围绕这个进行讨论:
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065
您的问题源于试图改变组件主体内部的 tooltipTarget
引用。您应该在您的上下文中声明另一个 setter 函数,您可以调用该函数来更改 ref.
的当前值
某个组件我有很多实例,我们称它为<Item>
。将鼠标悬停在任何 <Item>
上时,(quite 复杂且动态的)工具提示应出现在项目的位置。
因为我使用的是语义-ui-react,所以我选择的工具是使用Popup
来显示工具提示。使用弹出窗口的一种方法是给它们一个触发器组件(这将是我的 <Item>
)和一个内容组件,它会自动显示在触发器组件周围的特定位置。但是后来我的每个项目都有一个 Popup
实例,这显着增加了我的应用程序的响应时间,因为即使我目前不需要它们,也必须创建所有 Popup
s。
另一种使用 Popup
的方法是通过 context
属性 给它一个 ref,它将在那个 DOM 节点打开。因此,我想使用这种机制只有一个 Popup
,每当我将鼠标悬停在 <Item>
.[=35= 上时,它将接收对其 context
属性 的动态更新]
不过,为此,我需要将当前 <Item>
ref 传递给 Popup。因为我有一个深度嵌套的应用程序,所以我选择使用 https://github.com/diegohaz/constate 来全局共享状态。
不幸的是,共享 ref 似乎不起作用。每当我尝试从共享上下文中读取它时,它都是空的。
请看这里的简单示例: https://codesandbox.io/s/pplw689627
虽然我可以在调试器中看到 tooltipTarget
写入 onMouseEnter
,但当它在 <Tooltip>
组件中读取时,它始终为 null。
知道我做错了什么吗?
我愿意接受不同的建议,但如果可能的话,我更愿意使用 Semantic 的 Popup 以保持一致的视觉风格。
更新:
Tom Finneys 的回答在上面的沙箱中有效,不幸的是我在我的实际环境中使用 Typescript 并且它不能立即工作。我在这个沙盒中得到了一个有效的解决方案: https://codesandbox.io/s/7143r32klj
主要问题是 RefObject<T>
的 current
属性 是只读的。这可以防止您编写这样的上下文:
export function useTooltip() {
const [tooltipRecipe, setTooltipRecipe] = React.useState<string | undefined>(
undefined
);
let tooltipTarget = React.useRef<HTMLElement>(null);
const setTooltipTarget = (target: HTMLElement | null) => {
dummy.current = target;
};
return { tooltipRecipe, tooltipTarget, setTooltipRecipe, setTooltipTarget };
}
setTooltipTarget
无法编译。
我现在可以让它工作的唯一方法是将 RefObject<T>
强制转换为 current
不是只读的本地类型。这是 quite 一个黑客,但它有效。请参阅沙盒中的解决方案。
这里实际上正在围绕这个进行讨论: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065
您的问题源于试图改变组件主体内部的 tooltipTarget
引用。您应该在您的上下文中声明另一个 setter 函数,您可以调用该函数来更改 ref.