React-like refs in lit-html / lit-element?
Asked Answered
F

5

12

Does lit-html have by any change something like React's ref feature? For example in the following pseudo-code inputRef would be a callback function or an object { current: ... } where lit-html could pass/set the HTMLElement instance of the input element when the input element is created/attached.

// that #ref pseudo-attribute is fictional
html`<div><input #ref={inputRef}/></div>`

Thanks.

Fiore answered 26/11, 2019 at 2:11 Comment(3)
Since lit-html renders directly to the dom you don't really need refs like in React, you can just querySelector to get a reference to the rendered input, are you using lit-html only or are you using it in LitElement? I can post a more detailed answer with an example matching what you needOchlocracy
Thanks, I was afraid that this would be the answer to my question. Alan, please do me a favor and open a short answer, so you will get the credit for a correct anwer.Fiore
ref directive has been added to Lit lit.dev/docs/api/directives/#refPerfectible
F
7

As @WeiChing has mentioned somewhere above, since Lit version 2.0 you can use the newly added directive ref for that: https://lit.dev/docs/templates/directives/#ref

Fiore answered 6/10, 2021 at 4:53 Comment(0)
I
20

In lit-element you can use @query property decorator. It's just syntactic sugar around this.renderRoot.querySelector().

import { LitElement, html, query } from 'lit-element';

class MyElement extends LitElement {
  @query('#first')
  first;

  render() {
    return html`
      <div id="first"></div>
      <div id="second"></div>
    `;
  }
}
Idiopathy answered 24/1, 2020 at 22:30 Comment(1)
This solution seems to be the most succinct, direct, and useful.Vevina
F
7

As @WeiChing has mentioned somewhere above, since Lit version 2.0 you can use the newly added directive ref for that: https://lit.dev/docs/templates/directives/#ref

Fiore answered 6/10, 2021 at 4:53 Comment(0)
V
5

lit-html renders directly to the dom so you don't really need refs like you do in react, you can use querySelector to get a reference to the rendered input

Here's some sample code if you were only using lit-html

<html>

<head>
  <title>lit-html example</title>
  <script type="module">
    import { render, html } from 'https://cdn.pika.dev/lit-html/^1.1.2'; 
    const app = document.querySelector('.app'); 
    const inputTemplate = label => { 
      return html `<label>${label}<input value="rendered input content"></label>`;
    }; 
    // rendering the template
    render(inputTemplate('Some Label'), app);
    // after the render we can access it normally
    console.log(app.querySelector('input').value);
  </script>
</head>

<body>
  <div class="app"></div>
  <label>
    Other random input
    <input value="this is not the value">
  </label>
</body>

</html>

If you're using LitElement you could access to the inner elements using this.shadowRoot.querySelector if you're using shadow dom or this.querySelector if you aren't

Verbid answered 27/11, 2019 at 3:37 Comment(0)
F
2
-- [EDIT - 6th October 2021] ----------------------------
Since lit 2.0.0 has been released my answer below
is completely obsolete and unnecessary!
Please check https://lit.dev/docs/api/directives/#ref
for the right solution.
---------------------------------------------------------

Even if this is not exactly what I have asked for:

Depending on the concrete use case, one option to consider is the use of directives.

In my very special use-case it was for example (with a little luck and a some tricks) possible to simulate more or less that ref object behavior.

const inputRef = useElementRef() // { current: null, bind: (special-lit-html-directive) }
...
return html`<div><input ref=${inputRef.bind}/></div>`

In my use case I could do the following:

  1. Before rendering, set elementRef.current to null
  2. Make sure that elementRef.current cannot be read while the component is rerendered (elementRef.current is not needed while rendering and an exception will be thrown if someone tries to read it in render phase)
  3. That elementRef.bind directive will fill elementRef.current with the actual DOM element if available.
  4. After that, elementRef.current can be read again.
Fiore answered 27/11, 2019 at 4:33 Comment(1)
e.g. I converted react component A to litElement B. Is it a way to use A's internal methods. e.g. const myRef = useRef() <B ref={myRef}/>, myRef.current.innerMethod().Dobson
B
1

For lit-html v1, you can define your own custom Derivative:

import { html, render, directive } from "lit-html";
    
function createRef(){ return {value: null}; }
    
const ref = directive((refObj) => (attributePart) => {
  refObj.value = attributePart.committer.element;
});
    
const inputRef = createRef();
render(html`<input ref=${ref(inputRef)} />`;
     
// inputRef.value is a reference to rendered HTMLInputElement
Buddhology answered 4/3, 2022 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.