S
SolidJS•2y ago
Martnart

Forward reference a Component's state in Template

I try to wrap a Trigger and a Display component in a state managing component like so
export function Wrapper(props) {
const [state, setState] = createSignal(false)

return (
<>
{props.children}
<div>{state()}</div>
</>
)
}

export function Main() {
return (
<Wrapper>
<button onClick={() => Wrapper.setState(v => !v)}>Click</button> // Pseudo-code - Wrapper.setState does not make sense of course
</Wrapper>
)
}
export function Wrapper(props) {
const [state, setState] = createSignal(false)

return (
<>
{props.children}
<div>{state()}</div>
</>
)
}

export function Main() {
return (
<Wrapper>
<button onClick={() => Wrapper.setState(v => !v)}>Click</button> // Pseudo-code - Wrapper.setState does not make sense of course
</Wrapper>
)
}
Is this possible? Background is, I want the triggering component to be dynamic. Might be a button, checkbox, input field, etc. And I would like to avoid having to hardcode every event handler Maybe there's also a more straightforward way to achieve this instead of this approach? Cheers!
9 Replies
Martnart
Martnart•2y ago
Concrete use-case which might make the issue easier to understand: I have a Modal component and want to combine it with a dynamic Trigger component.
<ModalWrapper>
<Button/>
<Modal/>
</ModalWrapper>
<ModalWrapper>
<Button/>
<Modal/>
</ModalWrapper>
Wrapper is supposed to create the modal's open state and share it between the trigger and the modal itself - but Button should be replaceable with any other component. For this reason, I want to be able to hook into the Wrapper's setter from the template context. Probably not possible like this but there must be some way to achieve something similar?!
Otonashi
Otonashi•2y ago
SolidJS
Solid is a purely reactive library. It was designed from the ground up with a reactive core. It's influenced by reactive principles developed by previous libraries.
Martnart
Martnart•2y ago
Thanks for the suggestion, but it's not really what I'm looking for afaik. With a context the core of the question remains the same. I'd have to hard-code the handler of the trigger in a sub-component because I cannot access the created Context from the parent's JSX template. Unless I'm missing something
Otonashi
Otonashi•2y ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Martnart
Martnart•2y ago
Okay but if I want to replace Button with a Form , or Checkbox or whatever, I'd have to create an extra component for each of those cases. My goal is to not have to do that and dynamically bind the openModal state with whatever my component uses as a handler, e.g.
<WrapperContext>
<Form onSubmit={triggerModal}/>
<Modal/>
</WrapperContext>
<WrapperContext>
<Form onSubmit={triggerModal}/>
<Modal/>
</WrapperContext>
It's possible that it's just not do-able. But I was wondering if there is a way to make it happen
Otonashi
Otonashi•2y ago
not sure what you mean like so it accepts it as a prop?
Otonashi
Otonashi•2y ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Martnart
Martnart•2y ago
Not really. Just to be able to reference the created context from the outside scope before it's created. I don't know how else to describe it 😄 I know I could create a component for each of those cases individually, like
const FormModal = (props) => {
const context = useModalContext()
return (
<>
<Form onSubmit={context.trigger}/>
<Modal isOpen={context.isOpen}/>
</>
)
}
const FormModal = (props) => {
const context = useModalContext()
return (
<>
<Form onSubmit={context.trigger}/>
<Modal isOpen={context.isOpen}/>
</>
)
}
But then I have to create FormModal, ButtonModal, CheckboxModal, ... Which I wouldn't have to do if I could reference the state from the template itself, like I said above:
<Wrapper>
<WhateverComponent onWhateverEvent={triggerModal} />
<Modal/> // get open state from Wrapper
</Wrapper>
<Wrapper>
<WhateverComponent onWhateverEvent={triggerModal} />
<Modal/> // get open state from Wrapper
</Wrapper>
Your last example looks it can be used in the way I mean Thank you for your help. However, I can't get it to work. I don't think it's designed to work like that. Or I'm doing something wrong. With this jsx
<Wrapper>
<button onClick={() => triggerModal()}>Click!</button>
<Modal />
</Wrapper>
<Wrapper>
<button onClick={() => triggerModal()}>Click!</button>
<Modal />
</Wrapper>
Context works fine in Modal, but is undefined in the triggerModal callback :/
import { createContext, createSignal, useContext } from 'solid-js'

const WrapperContext = createContext()

export function Wrapper(props) {
const state = createSignal(false)

return <WrapperContext.Provider value={state}>{props.children}</WrapperContext.Provider>
}

export const useWrapperContext = () => useContext(WrapperContext)

export const triggerModal = () => {
const [, setIsOpen] = useWrapperContext()
return () => setIsOpen((v) => !v)
}
import { createContext, createSignal, useContext } from 'solid-js'

const WrapperContext = createContext()

export function Wrapper(props) {
const state = createSignal(false)

return <WrapperContext.Provider value={state}>{props.children}</WrapperContext.Provider>
}

export const useWrapperContext = () => useContext(WrapperContext)

export const triggerModal = () => {
const [, setIsOpen] = useWrapperContext()
return () => setIsOpen((v) => !v)
}
Otonashi
Otonashi•2y ago
you shouldn't be wrapping triggerModal() in a thunk, i.e.
<Wrapper>
<button onClick={triggerModal()}>Click!</button>
<Modal />
</Wrapper>
<Wrapper>
<button onClick={triggerModal()}>Click!</button>
<Modal />
</Wrapper>
also, the context will be undefined if you try to read it inside the event handler since that is called outside the context
Want results from more Discord servers?
Add your server