You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It's often useful to refer to one DOM element from another. So for example you may have a modal window Component/JSX, and you have a button that would call showModal on it. So you want the button to refer to the window.
Now, React Hooks presumably provides something similar… This is hinted by the existence of ref field in each JSX, and by useRef presence. But whether and how it works is completely unknown.
The useRef returns a data UseRef and Ref, but the former is an opaque data with zero functions for it, and the latter is just a container for an arbitrary value you'd pass over to the call. So I've no idea what it could be used for.
Similarly, the ref fields are supposed to contain some Node, but again that requires for a function JSX -> Node to exist, which it doesn't.
So what's the status of this feature, and what these ref and useRef are for anyway?
and the ref property of JSX elements takes a Ref (Nullable Node).
In regular React, the ref property is overloaded to also take a function (and at one point it also could take a string I think, but I think that's been deprecated). The overloading is not implemented in this library: ref must be Ref (Nullable Node). I don't know if that clears up the confusion.
So for example you may have a modal window Component/JSX, and you have a button that would call showModal on it. So you want the button to refer to the window.
Here's a minimal example of opening a dialog with showModal. The Try PureScript playground doesn't support FFI code, but if you were to do this for real, you'd want to have something like
So, like, the ref : ref initialization isn't actually initialization of the field, but instead it is initialization of the variable ref to make it point to the JSX? If so, I think it definitely deserves some documentation, because I would've never figured it out 😅
Although, no, even with unsafe magic I don't think you can make ref to get called as a function to perform effect during the field initialization. So no, I'm clearly misunderstanding how it works. Do you maybe create a ref, and then assign it to the field, so later it can be used from a button? That makes sense. But then why null is there?
Do you maybe create a ref, and then assign it to the field, so later it can be used from a button? That makes sense. But then why null is there?
However, if that is correct, that means that the readRefMaybe ref inside the handler doesn't actually just "reads the ref", but instead it performs a search over the DOM trying to find the element ref refers to. Which is quite possible seeing how it returns Effect …, but kinda contradicts to what's written in the name.
Another thing I don't understand is why readRefMaybe returns Effect (Maybe a) instead of Effect (Maybe Node). Had it been the latter, that would've provided at least some possibility to guess how one can use the refs.
In regular React, the ref property is overloaded to also take a function (and at one point it also could take a string I think, but I think that's been deprecated).
FWIW, I've never worked with React before starting to use this library (I wasn't even a web-programmer for that matter), so I'm coming to this library with completely clean mind 😊
Another thing I don't understand is why readRefMaybe returns Effect (Maybe a) instead of Effect (Maybe Node)
A Ref can be any value, it doesn't need to be Node. A Ref in React is just a mutable value that is scoped to the component that initializes it (the component where useRef is called). A very common use case is to reference DOM nodes, but something like this is also valid
mkCounter::Component{}
mkCounter = do
component "Counter" \_ ->React.do
ref <- useRef 0let
handleClick = Events.handler_ do
current <- readRef ref
writeRef ref (current + 1)
next <- readRef ref
Console.log ("You clicked " <> show next <> " times!")
pure $
R.div_
[ R.button
{ onClick: handleClick
, children: [ R.text "Click me!" ]
}
]
The ref is being initialized as null before the JSX is rendered, and then assigned to the dialog DOM node during render. Again, the official React docs are a good reference if you haven't read through them yet.
Okay, I see, thank you. Part of my confusion was due to the fact that null wraps some type a, but somehow useRef doesn't require specifying that type, and then during a JSX creation a becomes a Node. I am still confused how could that possibly work on the type-level, but I am presuming it's just an FFI magic (is that correct?).
So, how about adding to the library a helper function useRefNode that doesn't requires passing null and can be used exactly to create a reference to a JSX element? That would help immensely (along with some description of course), because right now the biggest problem IMO is not even lack of docs but that the types has no mention of Node anywhere (and in languages like Haskell and PureScript types help a lot in reading the code).
Part of my confusion was due to the fact that null wraps some type a, but somehow useRef doesn't require specifying that type, and then during a JSX creation a becomes a Node. I am still confused how could that possibly work on the type-level, but I am presuming it's just an FFI magic (is that correct?).
This is not really anything to do with FFI, or with React, this is just how type inference works in PureScript. Maybe it helps to think of a simpler example:
x = Nothingfoo::MaybeInt->String
foo = case _ ofJust1->"one"
_ ->"something else"
bar = foo x
The type of Nothing is forall a. Maybe a, but because foo takes Maybe Int, the compiler can infer that x has type Maybe Int. Similarly, because the type of ref here is Ref (Nullable Node), the compiler can infer the type of ref in
So, how about adding to the library a helper function useRefNode that doesn't requires passing null and can be used exactly to create a reference to a JSX element? That would help immensely (along with some description of course), because right now the biggest problem IMO is not even lack of docs but that the types has no mention of Node anywhere (and in languages like Haskell and PureScript types help a lot in reading the code).
And yeah, I can send a PR
While at it, I think it might also be useful to add an alias type RefNode = Ref (Nullable Node), because the full type is to large to type. Additionally, Node is a Web.DOM import whereas the alias would be just the React Hooks import. So yeah, I think it should be useful as well.
Hm, I'm not really sold on the value of adding a specialized useRefNode, and less inclined to add a type RefNode = Ref (Nullable Node) alias. It is possible to have a Ref Node, so the proposed naming could be confusing.
There is some precedent for having specialized hooks: useEffectOnce is the same as useEffect unit, I think. I would similarly be disinclined to add useEffectOnce if it didn't already exist though 😅. So I'm on the fence: if other maintainers or users of the library think this would be a valuable addition I would defer to them.
I think additional documentation would certainly be welcome, if that would serve the same purpose.
Refs are part of react, but accepting ref props as a way to externally expose dom elements is a feature from react-dom. As such, the helper you’re proposing would need to live there, not in this library. The type would be different for react-native, for example. Or if you’re writing a custom component which allows refs as props in some way.
This is one of those things that isn’t explained much here in the purescript library because its react stuff, and already documented there. I’d suggest reading up on their docs for dom refs and how/when to use them. That could be clearer here, but that’s the way most of this library is, assuming react knowledge.
The null is also important. It’s the value of the ref up until the child component you’re passing the ref to is rendered and mounted. It’s set asynchronously when the child is ready, not when the parent defining it renders. A bit weird, but it’s basically a callback, one that leaves the “when” up to you (usually a useEffect or event handler). You could also be putting the ref on a child which conditionally renders, which could reintroduce the null value.
I’d suggest reading up on their docs for dom refs and how/when to use them.
Unfortunately their docs don't contain purescript examples 😉 I did read them before asking on PureScript discorse, and then in absence of answers (which implies nobody there knew either) asking here. For PureScript ecosystem I think it's better to document the PureScript library. Especially so, since in my experience React Hooks library is way better than Halogen, but then Halogen is well documented, whereas React Hooks not so much.
I am planing to write documentation for refs to close this issue (if nobody beats me to it, which I'm completely fine with), just didn't get to it yet.
So, got some spare time, wrote documentation on how to use useRef. PR.
Seeing surprising opposition for adding helpers to the library, I decided to mention them in the docs instead, I hope it still will simplify some newbie's life in the future.
I'd actually be pro adding the NodeRef type alias, since I've created that same one for myself in the past, too, and I agree it's a pretty common use case.
Activity
pete-murphy commentedon Nov 5, 2024
Here's an example using
useRef: https://github.com/pete-murphy/demo-foreign-import-hooks/blob/main/src/App.purs#L20Sorry, I don't understand what this is saying. Binding
useRefgives you aRef a, and therefproperty of JSX elements takes aRef (Nullable Node).pete-murphy commentedon Nov 5, 2024
In regular React, the
refproperty is overloaded to also take a function (and at one point it also could take a string I think, but I think that's been deprecated). The overloading is not implemented in this library:refmust beRef (Nullable Node). I don't know if that clears up the confusion.pete-murphy commentedon Nov 5, 2024
Here's a minimal example of opening a
dialogwithshowModal. The Try PureScript playground doesn't support FFI code, but if you were to do this for real, you'd want to have something likeinstead of using
unsafeCoerce.https://try.purescript.org/?code=LYewJgrgNgpgBAIQgF2SAdgZwEowIYDGyAEiCANaYB0AsngJbpwDuAFjAE4wBQ39wABxAdkcAApcoEMDz6DhogCJ5keWngCeAI3gAKOtpi6qVAJSm5QkXGWqqAOWhQ8W2HF3onF-ldEBRADMAmCJ3QOCib3lrcJDkKj8ADwIYAWR6DHdkVg4QZijfOFxCeIQ8THoCKkUAeRo4cqLLBSL8IioyiqramioAYSh6GHRRXQIuFRhsMmQAGjgudBkOaZBkApbi9s7KqlIKTHc%2BkHl0Ybm4AhOhM5H5iEwpmAD5ibBcAIMdDest0vLdvtKA1Dn9mr82v8ugkAG7nQ6NPxwkaYcGiADqMC01TqDgwflgwHOYjwi2Q9nAegA5jBkASYESRggNABJMA-DFYvYAFRoABl3MxGGA8hy4JjscReXyefzFCACBBGaM0BT0PTlSSyRSZGKJbKZejhXl3CLFcq9VyelQWSNOOg8FAqNyNAIYIddDqYGLYu0AKroRUcDhDMDuDgQdVBOIAMXQAEYLGi4AHMHhgv0QJwUu5I2ngsds97eABaEutR1QDRwbL0Q7MEDQMM6OA%2BQnnGBhxhwGMxlncTCsPI0cCOuAALnHcC9cBLAD44L7RAH6MgB0PmCOwGP0JS4ABeBaRpdx%2BO5rDpmCFjg53e6qiD4ejqBwSOr3jABhMSeL6OhFdrp%2B3aHiK3BwHAZpwAAPOWZpKucB5QVBLDGswYELDM0HljSdLtkyrJhgARLkayEXAAAkNYgGqGrEqS5wzma6EEOU8AkaIIABOh4EUrW6BUrOC7ZLkzBwIRxxNnAu6iAEwoYWsVCEdxcAAFIPKIVwjF%2BnCCRBIDKeBExEKsogwZcEzIFMmGaaojCcAZDQCAIWFwMA5AAIJOQ5izLCZCyQn5uh4E5cAAN4AL5Jm5nnOT%2BxynAhEXcNFIUgfp4FXAlIxidy7rIGRAA6AD6ul-FQoHgYZzwuQ8TwBFJTjKcpsBrpVlUYAMlTkAei7Isg1CsHgSywBwJUVW1lVvB8XxsdVDltXOc6HixjxwCVnHzRNamYKIYD0I6IACfOm0TXAj6bs%2BEH7VAh0nZVvGsIwR1zndbVEoIyAaMpAgQFwFEOdg5X0DCRWbQA2kUVBaCgaBMHdoVwB1gwEOQd3zAQj1QGAixThDgOWYkoiETUbpMHtB1UmRAC6d3hZt8yA%2BTN1UvD-lcadbXo5j2PDLjkME0TijXYd1O05tNNAA
Hi-Angel commentedon Nov 5, 2024
Oh, thank you! So, am I correct in my understanding that this line initializes
refno basically nothing:and then the actual initialization happens here?
So, like, the
ref : refinitialization isn't actually initialization of the field, but instead it is initialization of the variablerefto make it point to theJSX? If so, I think it definitely deserves some documentation, because I would've never figured it out 😅Hi-Angel commentedon Nov 5, 2024
Although, no, even with unsafe magic I don't think you can make
refto get called as a function to perform effect during the field initialization. So no, I'm clearly misunderstanding how it works. Do you maybe create aref, and then assign it to the field, so later it can be used from a button? That makes sense. But then whynullis there?Hi-Angel commentedon Nov 5, 2024
However, if that is correct, that means that the
readRefMaybe refinside the handler doesn't actually just "reads the ref", but instead it performs a search over the DOM trying to find the elementrefrefers to. Which is quite possible seeing how it returnsEffect …, but kinda contradicts to what's written in the name.Hi-Angel commentedon Nov 5, 2024
Another thing I don't understand is why
readRefMaybereturnsEffect (Maybe a)instead ofEffect (Maybe Node). Had it been the latter, that would've provided at least some possibility to guess how one can use therefs.FWIW, I've never worked with React before starting to use this library (I wasn't even a web-programmer for that matter), so I'm coming to this library with completely clean mind 😊
pete-murphy commentedon Nov 5, 2024
A
Refcan be any value, it doesn't need to beNode. ARefin React is just a mutable value that is scoped to the component that initializes it (the component whereuseRefis called). A very common use case is to reference DOM nodes, but something like this is also validThat would be equivalent to this JS
The official React documentation goes into greater detail about
useRef: https://react.dev/reference/react/useRefHi-Angel commentedon Nov 5, 2024
So, suppose I used a
and then assigned
refto a JSXDoes that imply the
refnow contains two values at the same time:0that can be read byreadRefMaybe Nodethat can be read byreadRefMaybe?
pete-murphy commentedon Nov 5, 2024
No, that example wouldn't compile. In
refhas typeRef Int. Init has type
Ref (Nullable Node). You should get a type unification error if you try to compile that.pete-murphy commentedon Nov 5, 2024
Reassigning
refwill do just that: reassign it. Therefwon't "contain two values at the same time". InThe
refis being initialized asnullbefore the JSX is rendered, and then assigned to thedialogDOM node during render. Again, the official React docs are a good reference if you haven't read through them yet.Hi-Angel commentedon Nov 5, 2024
Okay, I see, thank you. Part of my confusion was due to the fact that
nullwraps some typea, but somehowuseRefdoesn't require specifying that type, and then during aJSXcreationabecomes aNode. I am still confused how could that possibly work on the type-level, but I am presuming it's just an FFI magic (is that correct?).So, how about adding to the library a helper function
useRefNodethat doesn't requires passingnulland can be used exactly to create a reference to a JSX element? That would help immensely (along with some description of course), because right now the biggest problem IMO is not even lack of docs but that the types has no mention ofNodeanywhere (and in languages like Haskell and PureScript types help a lot in reading the code).Hi-Angel commentedon Nov 5, 2024
And yeah, I can send a PR
pete-murphy commentedon Nov 5, 2024
This is not really anything to do with FFI, or with React, this is just how type inference works in PureScript. Maybe it helps to think of a simpler example:
The type of
Nothingisforall a. Maybe a, but becausefootakesMaybe Int, the compiler can infer thatxhas typeMaybe Int. Similarly, because the type ofrefhere isRef (Nullable Node), the compiler can infer the type of ref inHi-Angel commentedon Nov 6, 2024
While at it, I think it might also be useful to add an alias
type RefNode = Ref (Nullable Node), because the full type is to large to type. Additionally,Nodeis aWeb.DOMimport whereas the alias would be just the React Hooks import. So yeah, I think it should be useful as well.Hi-Angel commentedon Nov 7, 2024
@pete-murphy so what do you think, would you accept such change?
Add helpers and documentation for `ref`erring DOM elements
pete-murphy commentedon Nov 8, 2024
Hm, I'm not really sold on the value of adding a specialized
useRefNode, and less inclined to add atype RefNode = Ref (Nullable Node)alias. It is possible to have aRef Node, so the proposed naming could be confusing.There is some precedent for having specialized hooks:
useEffectOnceis the same asuseEffect unit, I think. I would similarly be disinclined to adduseEffectOnceif it didn't already exist though 😅. So I'm on the fence: if other maintainers or users of the library think this would be a valuable addition I would defer to them.I think additional documentation would certainly be welcome, if that would serve the same purpose.
Hi-Angel commentedon Nov 8, 2024
Why? You yourself mentioned that it is frequent usecase for
useRefhook, so why not provide a helper to make using the library simpler?We can chose another naming. Like
RefDOMElement, or anything you'd like 😊maddie927 commentedon Nov 14, 2024
Refs are part of react, but accepting ref props as a way to externally expose dom elements is a feature from react-dom. As such, the helper you’re proposing would need to live there, not in this library. The type would be different for react-native, for example. Or if you’re writing a custom component which allows refs as props in some way.
This is one of those things that isn’t explained much here in the purescript library because its react stuff, and already documented there. I’d suggest reading up on their docs for dom refs and how/when to use them. That could be clearer here, but that’s the way most of this library is, assuming react knowledge.
The null is also important. It’s the value of the ref up until the child component you’re passing the ref to is rendered and mounted. It’s set asynchronously when the child is ready, not when the parent defining it renders. A bit weird, but it’s basically a callback, one that leaves the “when” up to you (usually a useEffect or event handler). You could also be putting the ref on a child which conditionally renders, which could reintroduce the null value.
Not sure of that helps
Hi-Angel commentedon Nov 14, 2024
Okay, gotcha.
Unfortunately their docs don't contain purescript examples 😉 I did read them before asking on PureScript discorse, and then in absence of answers (which implies nobody there knew either) asking here. For PureScript ecosystem I think it's better to document the PureScript library. Especially so, since in my experience React Hooks library is way better than Halogen, but then Halogen is well documented, whereas React Hooks not so much.
I am planing to write documentation for refs to close this issue (if nobody beats me to it, which I'm completely fine with), just didn't get to it yet.
Document what `useRef` may be used for and how
Hi-Angel commentedon Dec 11, 2024
So, got some spare time, wrote documentation on how to use
useRef. PR.Seeing surprising opposition for adding helpers to the library, I decided to mention them in the docs instead, I hope it still will simplify some newbie's life in the future.
i-am-the-slime commentedon Dec 14, 2024
I'd actually be pro adding the
NodeReftype alias, since I've created that same one for myself in the past, too, and I agree it's a pretty common use case.