Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
React Anchor References

/* The Anchor Component What is this, and how is it used?

The problem we had at work was to create a navigation menu, but we didn't know until runtime what components would be on the page. Added difficulty: We were server-side rendered, so we somehow needed to create a method that would register each component as it mounted on the page in order to get the position of each, store that in a central store somehow, so that we could create the buttons that would call a smooth-scroll to nav.

To solve this, this Anchor component is just included as a renderable JSX element in components that might be navigated to. It's an invisible div tag, so it won't show anything to the user visibly. But that tag will generate a React.Ref, (anchorRef) which will allow us to get the element (and it's position) from the DOM after render time.

We store these anchors as a property of the State Manager context, as well as add a method the Anchor component will need - 'makeAnchor' - which will register a ref Recordin the anchors context upon mount of each Anchor. This allows you to have a central repository for every anchor, allowing you to easily scroll to that point.

To use this, all you have to do is import somewhere in your component. In navigational components where you might need access to the record of anchors currently stored in the state manager, just:

  const { anchors } = useContext(StateManagerContext);
  const anchorState: Record<string, React.Ref<any>> = anchors;

Note that if you use an anchor but pass in undefined or other falsey value as the anchorId, it will render the div but NOT register the anchor in the state manager. However, if that anchorId becomes truthy (due to a change in state or props), it will re-render and run the useEffect, registering the value in the State Manager. */

import React, { useRef, useContext, useEffect } from "react";
interface AnchorProps {
anchorId?: string;
}
const Anchor: React.FunctionComponent<AnchorProps> = ({ anchorId }) => {
const anchorRef = useRef(null);
const { makeAnchor } = useContext(StateManagerContext);
useEffect(() => {
if (anchorId && anchorRef) {
makeAnchor(anchorId, anchorRef);
}
}, [anchorRef, makeAnchor, anchorId]);
return <div style={{visibility: 'hidden'}} ref={anchorRef} id={anchorId} />;
};
export default Anchor;
// /src/Component/Destination.tsx
import React from 'react'
import Anchor from '/src/Components/Anchor';
const Destination = (props?: any) => {
return <div {...props}>
<Anchor anchorId={'destination'}/>
</div>
}
export default Destination
import React, {useContext} from 'react';
// this could be any behavior that needs a ref - this is just a smooth scroll.
const handleScrollTo = (anchorRef: React.Ref<any>) => (): void => {
const current = anchorRef?.current;
if(!!current){
const top: number = window.pageYOffset + current?.getBoundingClientRect?.()?.top;
if(top !== undefined) {
window.scroll({
top,
left: 0,
behavior: 'smooth'
});
}
}
}
const OriginButton = ({anchorName}: {anchorName: string}) => {
const {anchors} = useContext(StateManagerContext);
return <button onClick={handleScrollTo(anchors[anchorName])}/>
}
export default OriginButton;
import React, { createContext, useReducer } from "react";
const StateManagerContext = createContext<any>({});
const Provider = StateManagerContext.Provider;
export const StateManagerProvider = ({ children }) => {
const [anchorState, dispatchAnchor] = useReducer(anchorReducer, {})
const makeAnchor = (
anchorId: string,
ref: React.Ref<any>
): void => {
if (!!ref && anchorId) {
dispatchAnchor([anchorId, ref]);
}
};
return <Provider value={{ anchors: anchorState, makeAnchor }}>{children}</Provider>;
};
export default StateManagerContext;
import { Ref } from 'react';
type AnchorTuple = [string, Ref<any>];
const anchorReducer = (
state: Record<string, Ref<any>>,
action: AnchorTuple | undefined
) => {
if (!action) {
return state;
}
const [id, ref] = action;
if (state[id] === ref) {
return state;
}
return { ...state, [id]: ref };
};
export default AnchorReducer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.