import React, { createContext, useContext, useReducer } from "react"; // 1️⃣ const initialState = { containerRef: null, stickyRefs: new Map(), }; // No operation const noop = () => {}; // 2️⃣ const initialDispatch = { setContainerRef: noop, addStickyRef: noop }; const StickyStateContext = createContext(initialState); const StickyDispatchContext = createContext(initialDispatch); const ActionType = { setContainerRef: "set container ref", addStickyRef: "add sticky ref", }; // 3️⃣ function reducer(state, action) { const { type, payload } = action; switch (type) { case ActionType.setContainerRef: // Reassigning a new ref, will infinitely re-load! return Object.assign(state, { containerRef: { current: payload.containerRef } }); case ActionType.addStickyRef: const { topSentinelRef, bottomSentinelRef, stickyRef } = payload; state.stickyRefs.set(topSentinelRef.current, stickyRef); state.stickyRefs.set(bottomSentinelRef.current, stickyRef); return Object.assign(state, { stickyRefs: state.stickyRefs }); default: return state; } } // 4️⃣ function StickyProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); const setContainerRef = containerRef => dispatch({ type: ActionType.setContainerRef, payload: { containerRef } }); const addStickyRef = (topSentinelRef, bottomSentinelRef, stickyRef) => dispatch({ type: ActionType.addStickyRef, payload: { topSentinelRef, bottomSentinelRef, stickyRef } }); // 5️⃣ const actions = { setContainerRef, addStickyRef }; return ( <StickyStateContext.Provider value={state}> <StickyDispatchContext.Provider value={actions}> {children} </StickyDispatchContext.Provider> </StickyStateContext.Provider> ); } // 6️⃣ function useStickyState() { const context = useContext(StickyStateContext); if (context === undefined) throw Error('"useStickyState should be used under "StickyStateContext'); return context; } // 7️⃣ function useStickyActions() { const context = useContext(StickyDispatchContext); if (context === undefined) throw Error( '"useStickyActions should be used under "StickyDispatchContext' ); return context; } const initialSectionValues = { topSentinelRef: null, bottomSentinelRef: null }; // 8️⃣ const StickySectionContext = createContext(initialSectionValues); export { StickyProvider, useStickyState, useStickyActions, ActionType, StickySectionContext };