useRef current 仅在第二次更新时获取其值

useRef current getting its value only on second update

我有以下组件:

const ParentComponent: React.FC = () => {
    const networkRef: any = useRef();

    // Somewhere in the code, I call this
    networkRef.current.filter(["id0, id1, id2"]);

    return (
    ...
        <VisNetwork 
            ref={networkRef}
        />
    ...
    )
}
export default ParentComponent;

interface Props {
    ref: any;
}
const VisNetwork: React.FC<Props> = forwardRef((props: Props, ref) => {
    useImperativeHandle(ref, () => ({
        filter(items: any) {
            setFilterNodes(items);
            nView.refresh();
        }
    }));

    const [filterNodes, setFilterNodes] = useState<any[]>([]);
    const filterNodesRef = useRef(filterNodes);
    useEffect(() => {
        filterNodesRef.current = filterNodes;
    }, [filterNodes]);

    ...
    // Some code to create the network (concentrate on the nodesView filter method)
    const [nView, setNView] = useState<DataView>();
    const nodesView = new DataView(nodes, {
        filter: (n: any) => {
            if (filterNodesRef.current.includes(n.id)) {
                return true;
            }
            return false;
        }
    })
    setNView(nodesView);
    const network = new vis.Network(container, {nodes: nodesView, edges: edgesView}, options);
});
export default VisNetwork;

当我调用 network.current.filter([...]) 时,它会设置 filterNodes 状态。此外,它应该在 useEffect.

中设置 filterNodesRef

但是,filterNodesRef.current 仍然是空数组。

但是当我第二次调用network.current.filter([...])时,只有filterNodesRef.current得到了值,DataView才能够过滤。

为什么会这样?我认为 useRef.current 将始终包含最新值。

引用的 .current 设置不会将更改通知组件。一定有其他原因让它第二次起作用。

来自https://reactjs.org/docs/hooks-reference.html#useref

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.

您可能想要使用 useState,因为这会重新呈现组件。

还有两件事

  1. 我不太确定 networkRef.current.filter(["id0, id1, id2"]) 是什么。当我尝试做 ['a'].filter(['a']) 时,Typescript 确实会抱怨,但我从未见过这个,所以你确定这是你想要做的吗?

  2. 如果您要传递引用,可能有更好的方法。也许考虑重新考虑组件之间的关系。您这样做是因为您需要访问多个组件内的 networkRef 吗?如果是,您可能需要查看 providers.

如果这不能回答您的问题,请发表评论(请针对具体问题),我很乐意为您提供帮助:)

是的,useRef.current 包含最新值,但是 useEffect 中的 filterNodesRef.current 这就是为什么在初始渲染中得到空数组的原因。

  • VisNetwork 的初始渲染 filterNodes 是一个空数组 ==> filterNodesRef.current 仍然是空的。因为 setFilterNodes(items); 是异步函数 => 你在 useImperativeHandle 中设置的事件,它将在第二次渲染时更新。

  • useImperativeHandle 中你设置 setFilterNodes(items); ==> filterNodes 被更新并且 VisNetwork 重新渲染 ==> useEffect 被触发 = => filterNodesRef.current 设置为新的 filterNodes

让我们试试这个:

.... 
const filterNodesRef = useRef(filterNodes);
useImperativeHandle(ref, () => ({
    filter(items: any) {
      filterNodesRef.current = filterNodes;
      setFilterNodes(items);
      nView.refresh();
    }
}));
...

我最终通过在 useEffect 中调用 refresh() 方法而不是 filter() 方法解决了这个问题:

useEffect(() => {
    filterNodesRef.current = filterNodes;
    nView.refresh();
}, [filterNodes]);