I'm using forwardRef to pass a modalRef from the component "AllPosts" -> "Posts" -> "Post", However, when I try to access the ref value I'm always getting null. The idea is to forward the ref of the modal so I can open it from the button in "Post" component with its data.
Demo:stackblitz
I have tried using this {modalRef && } but then the Posts component doesn't load.
If you just want to open a modal you don't need to force a ref structure.
E.g. with states it could look like this:
function Parent() {
const [modalOpen, setModalOpen] = useState(false);
const flipModalOpen = useCallback(() => setModalOpen(p => !p), []);
return <Child modalOpen={modalOpen} flipModalOpen={flipModalOpen} />
}
function Child({ modalOpen, flipModalOpen }: { modalOpen: boolean, flipModalOpen: () => void }) {
return <ChildOfChild modalOpen={modalOpen} flipModalOpen={flipModalOpen} />
}
function ChildOfChild({ modalOpen, flipModalOpen }: { modalOpen: boolean, flipModalOpen: () => void }) {
return <>
<button style={{ display: modalOpen ? "none" : null }} onClick={flipModalOpen}>Open Modal</button>
<div style={{ display: modalOpen ? null : "none" }}>
{/* Modal content here*/}
<button onClick={flipModalOpen}>Close Modal</button>
</div>
</>
}
The biggest drawback of this is that the state is in a parent and not in the component itself.
Looking at your code i see a few thing i'd assume to be wrong. E.g. You modalRef variable on AllPosts is assigned to different components.
Further i think there might be a logic misconception. The forwardRef is a method to reference the component in a different one. You using the reference inside the component & reassing the same reference to everything.
Imagine it like this: A reference is a variable with a pointer to one specific instance of your Post Component. Then however, multiple post components are rendered in the Posts Component. All using the same variable.
So something like this is happening "somewhere" in react
let varRef = <Post> Modal 1 <Post/>;
varRef = <Post> Modal 2 <Post/>;
varRef = <Post> Modal 3 <Post/>;
so when you try to access the current it most likely is not what you are looking for.
Your solution will depend on your usecase and where the states are meant to be stored etc. Assuming i have n modals that are pretty much the same I'd create one instance of the modal and only change the required values in states (open & text) that i pass in
export function Modal({open,setOpen,text}) {
return (
<div className={`fixed top-0 left-0 w-full h-full bg-black bg-opacity-50 ${open ? 'block' : 'hidden'}`}>
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white p-5">
<div>{text}</div>
<button onClick={()=>setOpen(false)} className="bg-red-500 text-white">close</button>
</div>
</div>
)
}
The ref idea is valid for some scenarios. However you will always need the ref itself. Could be build like this
export function Parent({ modalArray }) {
const allModalRefs = useRef(modalArray);
// you can access the elements with allModalRefs.current[n]
useEffect(() => {
allModalRefs.current = allModalRefs.current.slice(0, modalArray.length);
}, [modalArray]);
return modalArray.map((m, i) => (
<Fragment key={m.id} >
<button onClick={() => {
const modal = allModalRefs.current[i];
modal.setOpen(true);
modal.setText(m.text)
}}>open {i}</button>
<Modal ref={el => allModalRefs.current[i] = el} />
</Fragment>
))
}
const Modal = forwardRef(({ }, ref) => {
const [open, _setOpen] = useState(false);
const [text, _setText] = useState('');
useImperativeHandle(ref, () => ({
setOpen(value: boolean) { _setOpen(value); },
setText(value: string) { _setText(value); }
}));
return <div className={`fixed top-0 left-0 w-full h-full bg-black bg-opacity-50 ${open ? 'block' : 'hidden'}`}>
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white p-5">
<div>{text}</div>
<button onClick={() => _setOpen(false)} className="bg-red-500 text-white">close</button>
</div>
</div>
});
Hope it helps :)