How do i pass data up from a child web component to it's direct parent element
Asked Answered
C

3

6

I need to basically pass a value down to my web component, I will then do some random thing to it, now I want to pass that new value back up to my React Comnponent. How do I do that?

function MyReactcomp() {
   const [state, setState] = useState("Justin is cool");
   return (
        <div className="Wrapper">
           <customweb-component state={state} />
        </div>
    );
}

Now inside the customweb-component, I will change state to "No your not!!". How can I pass this value back up from my web component to my Rea t Copmponent? I know I can pass a function down because you can only pass strings down

Celiotomy answered 21/5, 2022 at 0:15 Comment(7)
So are you using React or Web Components?Inainability
Both my friend, both. This is were its getting trickyCeliotomy
What does your web component look like (the JavaScript controller)?Inainability
@Inainability The controller is basically a useEffect in this component. this is where I would listen for an event from my web componentCeliotomy
i posted my answer below @InainabilityCeliotomy
Good for you! Just wondering, why are you using web components in React?Inainability
@Inainability lol fair question, my current project is react, but we have teams using angular and mobile. So I wanted to create a library for us to use across all our company platforms. Its only a POC so farCeliotomy
G
2

Instead of querying for DOM, you should use React's Ref as shown below:

function MyReactcomp() {
  const [state, setState] = useState("Justin is cool");

  // Hold the web component reference here
  const myWebComp = useRef();

  useEffect(() => {
    myWebComp.current?.addEventListener('tab-select', (event) => {
      setState(Object.keys(adminTabs[event.detail.tabIndex]));
    });
  }, []);

  return (
    <div className="Wrapper">
      <customweb-component ref={myWebComp} state={state} />
    </div>
  );
}

And, you if you need to observe for changes in your referenced element, then you can use plain useState to hold reference to the element:

function MyReactcomp() {
  const [state, setState] = useState("Justin is cool");

  // Hold the web component reference here
  const [webComp, setWebComp] = useState(null);

  useEffect(() => {
    webComp?.addEventListener('tab-select', (event) => {
      setState(Object.keys(adminTabs[event.detail.tabIndex]));
    });
  }, [webComp]);

  return (
    <div className="Wrapper">
      <customweb-component ref={setWebComp} state={state} />
    </div>
  );
}

If you find doing this too many times, you can abstract this behavior into custom hooks. For example:

function useWebCompProp(ref, initialValue) {
  const [state, setState] = useState(initialValue);

  useEffect(() => {
    ref.current?.addEventListener('onState', (event) => {
      // Update the state here
      setState(event.detail);
    });
  }, []);

  const setter = (newState) => {
    setState(newState);
    ref.current?.state = newState;
  };

  return [state, setter];
}


function useWebCompEvent(eventName, callback) {

  // Hold the web component reference here
  const myWebComp = useRef();

  useEffect(() => {
    myWebComp.current?.addEventListener(eventName, callback);
  }, []);

  return myWebComp;
}


function MyReactcomp() {

  const myWebComp = useWebCompEvent(eventName, (event) => {
    setState(Object.keys(adminTabs[event.detail.tabIndex]));
  });

  const [state, setState] = useWebCompProp(myWebComp, "Justin is cool");

  return (
    <div className="Wrapper">
      <customweb-component ref={myWebComp} />
    </div>
  );
}
Graphitize answered 23/5, 2022 at 4:38 Comment(7)
ok so this gives you access to the very top layer of the web component, now how do you cleanly extract data from the web components' children? You would still have to attach an event listener to the ref and then you would need to use lots of ref.children's to access any child right? The point of my question was basically how to achieve 2 way data using a web components inside of react.Celiotomy
I do think you maybe on the right track thoughCeliotomy
@JustinMeskan To achieve that, you need to abstract your common behavior into custom hooks. Updated my answer for sample reference.Graphitize
one question, the attribute "ref" what data type is it? If it's a function won't the web component convert it to a string and lose all scoping?Celiotomy
@JustinMeskan React shall take care of it.Graphitize
So the issue now is the under lying web -component doesn't update it's own internal state. The first way you had it with the useRef() that way work 100%. This updated way actually causes bugs.Celiotomy
That's correct because I am not setting initial state in second generic approach. You will probably have to play around with it to make it suit to your needs. I just provided a generic abstraction.Graphitize
C
2

I figured out that you need to attach a custom event handler to an element in your Lit-html template. You can then use getElementById or something to that effect and listen for the event in the parent component.

This is a combination of my answer and Harshal Patil's he took into account that using Reacts built-in features for manipulating the DOM is preferable to manipulating it directly.

import React, { useEffect, useRef } from 'react';

function MyReactcomp() {
   const [state, setState] = useState("Justin is cool");
   const webCompRef = useRef();
   useEffect(() => {
       webCompRef.addEventListener('tab-select', (event) => {
           setState("No he is not!!")
       })    

   }, []}
    return (
         <div className="Wrapper">
              <customweb-component ref={webCompRef} state={state} />
         </div>
     );
}

This pattern will allow you to pass your data down through the web components attributes. You can then listen for your native or custom events. This is cool because now components can cross frameworks and libraries, if one team is using React and the other is using Angular they can share components and keep there UI in sync.

Celiotomy answered 21/5, 2022 at 23:8 Comment(2)
Also remember, in custom events you can pass the data with event.detail.Inainability
@Inainability exactly!!Celiotomy
G
2

Instead of querying for DOM, you should use React's Ref as shown below:

function MyReactcomp() {
  const [state, setState] = useState("Justin is cool");

  // Hold the web component reference here
  const myWebComp = useRef();

  useEffect(() => {
    myWebComp.current?.addEventListener('tab-select', (event) => {
      setState(Object.keys(adminTabs[event.detail.tabIndex]));
    });
  }, []);

  return (
    <div className="Wrapper">
      <customweb-component ref={myWebComp} state={state} />
    </div>
  );
}

And, you if you need to observe for changes in your referenced element, then you can use plain useState to hold reference to the element:

function MyReactcomp() {
  const [state, setState] = useState("Justin is cool");

  // Hold the web component reference here
  const [webComp, setWebComp] = useState(null);

  useEffect(() => {
    webComp?.addEventListener('tab-select', (event) => {
      setState(Object.keys(adminTabs[event.detail.tabIndex]));
    });
  }, [webComp]);

  return (
    <div className="Wrapper">
      <customweb-component ref={setWebComp} state={state} />
    </div>
  );
}

If you find doing this too many times, you can abstract this behavior into custom hooks. For example:

function useWebCompProp(ref, initialValue) {
  const [state, setState] = useState(initialValue);

  useEffect(() => {
    ref.current?.addEventListener('onState', (event) => {
      // Update the state here
      setState(event.detail);
    });
  }, []);

  const setter = (newState) => {
    setState(newState);
    ref.current?.state = newState;
  };

  return [state, setter];
}


function useWebCompEvent(eventName, callback) {

  // Hold the web component reference here
  const myWebComp = useRef();

  useEffect(() => {
    myWebComp.current?.addEventListener(eventName, callback);
  }, []);

  return myWebComp;
}


function MyReactcomp() {

  const myWebComp = useWebCompEvent(eventName, (event) => {
    setState(Object.keys(adminTabs[event.detail.tabIndex]));
  });

  const [state, setState] = useWebCompProp(myWebComp, "Justin is cool");

  return (
    <div className="Wrapper">
      <customweb-component ref={myWebComp} />
    </div>
  );
}
Graphitize answered 23/5, 2022 at 4:38 Comment(7)
ok so this gives you access to the very top layer of the web component, now how do you cleanly extract data from the web components' children? You would still have to attach an event listener to the ref and then you would need to use lots of ref.children's to access any child right? The point of my question was basically how to achieve 2 way data using a web components inside of react.Celiotomy
I do think you maybe on the right track thoughCeliotomy
@JustinMeskan To achieve that, you need to abstract your common behavior into custom hooks. Updated my answer for sample reference.Graphitize
one question, the attribute "ref" what data type is it? If it's a function won't the web component convert it to a string and lose all scoping?Celiotomy
@JustinMeskan React shall take care of it.Graphitize
So the issue now is the under lying web -component doesn't update it's own internal state. The first way you had it with the useRef() that way work 100%. This updated way actually causes bugs.Celiotomy
That's correct because I am not setting initial state in second generic approach. You will probably have to play around with it to make it suit to your needs. I just provided a generic abstraction.Graphitize
B
1

In react, the transfer of data is 1-way only, i.e., from Parent-to-Child However, if you need to have a handler in the parent element for some data change in the child element, you can use Lifting Up State This could help achieving what you need.

If not, please provide more details about the actual case scenario

Barquisimeto answered 21/5, 2022 at 0:21 Comment(3)
React's state is different from Web Components.Inainability
Does this lift and shift only work with React? I might be pushing the limits of web components.Celiotomy
Pushing the limits of Web Components? Its React that fails: custom-elements-everywhere.com/libraries/react/results/…Wellgrounded

© 2022 - 2024 — McMap. All rights reserved.