How to pass callback function to litelement from React? - javascript

I'm trying to use lit-element component in my React project, and I'd like to pass in the callback function to lit-element component from React but with no luck.
I've tried a couple of different ways, like change property type, and pass function in as a string, but none of them works.
lit-element component code:
import { LitElement, html } from "lit-element";
class MyButton extends LitElement {
static get properties() {
return {
clickHandler: {
type: String
},
bar: {
type: String
}
};
}
render() {
const foo = this.clickHandler; //value is undefined
const bar = this.bar; //value is "it's bar"
return html`
<button #click=${this.clickHandler}>click me</button>
`;
}
}
customElements.define("my-button", MyButton);
react side code:
<my-button clickHandler={() => alert("clicked")} bar="it's bar" />
I put a break point in the render section of the component, and I can see the 'bar' value get passed in correctly, but the value of 'clickHandler' is undefined.
Does anyone have any idea on how to pass function from React to lit-element?
Thanks!

This question probably isn't just valid for react. It's quite general, how do I pass a handler from parent to child component, either it's a lit-element or html element.
According to the property doc, https://lit-element.polymer-project.org/guide/properties
<my-element
mystring="hello world"
mynumber="5"
mybool
myobj='{"stuff":"hi"}'
myarray='[1,2,3,4]'>
</my-element>
Doesn't seem that it supports (callback) function at the moment. So how does Element handle event from parent level ?
According to the event doc, https://lit-element.polymer-project.org/guide/events, you can dispatch any event to the dom tree, including your parent. Dom event system is much broader than React prop system.
class MyElement extends LitElement {
...
let event = new CustomEvent('my-event', {
detail: {
message: 'Something important happened'
}
});
this.dispatchEvent(event);
}
and then in either lit or non-lit context, use the following to handle the event,
const myElement = document.querySelector('my-element');
myElement.addEventListener('my-event', (e) => {console.log(e)});
This way you can allow children to fire implementation for parent, which is exactly the definition for callback.

What I found that works is adding a ref in the react component to the lit element, then literally setting the property on it.
So, for the following JSX:
<some-webcomponent ref={this.myRef}></some-webcomponent>
You can pass a property to ‘some-webcomponent’ in i.e. componentDidMount:
componentDidMount () {
const element = this.myRef.current;
element.someCallback = () => // ...
}
It’s not too pretty, but I wouldn’t consider it a hack either. Requires quite a lot of boilerplate though :/
Here’s a full React component for reference:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <some-webcomponent ref={this.myRef}></some-webcomponent>;
}
componentDidMount() {
const element = this.myRef.current;
element.someCallback = () => console.log(“call!”);
}
}
Where the lit element is:
import { LitElement, html } from "lit-element";
class SomeWebcomponent extends LitElement {
static get properties() {
return {
someCallback: { type: Function }
};
}
render() {
return html`
<button #click=${this.someCallback}>click me</button>
`;
}
}
customElements.define("some-webcomponent", SomeWebcomponent);

Have a look at the menu button click function for the no redux pwa-starter-kit at https://github.com/Polymer/pwa-starter-kit/blob/template-no-redux/src/components/my-app.js. I believe that provides the example that may work for you.

Related

How can I access a component's class properties from another component with React?

If I create a class like
class Foo {
constructor(bar) {
this.bar = bar;
}
}
let foo = new Foo('baz');
console.log(foo.bar); // baz
I am able to access the .bar property without issue. However, I have the following code split into two React components (trimmed for clarity):
A Card Component
class Card extends React.Component {
constructor(props) {
super(props);
this.name = props.cardData.name;
this.image = props.cardData.image_uris.png;
}
}
A Search Component
class Search extends React.Component {
constructor(props) {
super(props);
this.fuse = null;
this.state = {
show: false,
query: null,
pool: [
<Card cardData={{ name: 'test', image_uris: { png: 'https://i.imgur.com/or94i38.jpg' } }}></Card >,
<Card cardData={{ name: 'test', image_uris: { png: 'https://i.imgur.com/or94i38.jpg' } }}></Card >,
<Card cardData={{ name: 'test', image_uris: { png: 'https://i.imgur.com/or94i38.jpg' } }}></Card >,
<Card cardData={{ name: 'test', image_uris: { png: 'https://i.imgur.com/or94i38.jpg' } }}></Card >,
],
results: null
}
this.getResults = this.getResults.bind(this);
}
componentDidMount() {
this.fuse = new Fuse(this.state.pool, {
keys: ['name']
})
for (let x of this.state.pool) {
console.log(x.name); // undefined!
}
}
I'm unable to access the properties which I defined earlier on the Card component, but I'm not sure why. I've tried creating getters & moving the properties outside of the constructor to no avail. I'm sure I'm missing something simple with React or JS in general, but what?
You can pass data (Property Values) between components using observables (service oriented). This will assign the component property value(s) to an object that is then passed through subscription to another component that has subscribed to that observable.
import { Subject } from 'rxjs';
const subject = new Subject();
export const messageService = {
sendMessage: message => subject.next({ text: message }),
clearMessages: () => subject.next(),
getMessage: () => subject.asObservable()
};
Then your component code:
componentDidMount() {
// subscribe to home component messages
this.subscription = messageService.getMessage().subscribe(message => {
if (message) {
//Do stuff if you have the observable content
} else {
//Do other stuff is there is no observable content
}
});
Thanks for the help everyone. Per Felix's comment on my OP, I realized that my problem was how I was thinking about state and data flow in React in general. I was passing around Components rather than data.
Rather than use Refs (improperly) or add a new dependency, I opted to pass the data through state and props to the child component, and then render the data afterwards.
You can't do it. React only able to pass data to its parent to child but never be able to pass data from child to parent nor node to siblings (as far as I know).
so the way on how you would do it probably you will use Redux or you can also make a Global Variable you name it as storage and define your property for your component in that store. later on any data which you will pass you will refer to this Global Variable. But I don't think so this is the good practice. The formal practical one that I know is by using Redux.

How to detect click outside of the React functional component

I want to detect click outside of the React functional component.
Then I found the following article and implemented code accordingly.
Detect click outside React component
But my code does not work. I understand the cause, however don't know solution.
import React from 'react';
import Foo from 'foo'; // <- Functional component in the node_modules
export default class extends React.PureComponent {
private readonly refRoot = React.createRef();
public componentDidMount() {
document.addEventListener('click', this.clickOutside);
}
public componentWillUnmount() {
document.removeEventListener('click', this.clickOutside);
}
private clickOutside(ev: MouseEvent) {
if (refRoot.current.contains(ev.target)) {
console.log('clicked outside.');
}
}
public render(){
return (
<Foo ref={this.refRoot}> // <- Doubt.
...
</Foo>
);
}
}
Because cannot use the ref attribute on function component.
Maybe able to solve it by wrapping it with a div element, but I want to avoid complicating the stratum of the DOM any more when rendered to HTML.
What do you have any ideas?
If you want to use ref inside of functional component, then rather than passing it from parent, you can use React hooks API.
https://reactjs.org/docs/hooks-reference.html#useref
Please check the above ^^
The idea to create a ref on the DOM element rendered inside of Foo which you can do by passing it as a props which can be used in Foo
<Foo innerRef={this.refRoot}>
...
</Foo>
Inside Foo:
const Foo = (props) => {
return <div ref={props.innerRef}>{/* data here */}</div>
}
or you could use React.forwardRef to forward Foos ref to its children
const Foo = React.forwardRef(props, ref) => {
return <div ref={ref}>{/* data here */}</div>
}
Parent
public render(){
return (
<Foo ref={this.refRoot}> // <- Doubt.
...
</Foo>
);
}

Webcomponent Custom Element pass function onClick

I want to pass a function to Custom Element using ReactJS
ReactJS Component
import React from 'react';
import './SampleComponent';
export class Button extends React.Component {
handleClick = (event) => {
console.log(event)
}
render() {
return (
<div>
<sample-component onclick="handleClick"></sample-component>
</div>
)
}
};
Custom Element
class SampleComponent extends HTMLElement {
static get observedAttributes() {
}
// custom methods
render() {
this.innerHTML = `Custom Element`;
}
// lifecycle hooks
connectedCallback() {
this.render();
}
}
window.customElements.define('sample-component', SampleComponent);
As I understand, when I pass onclick function handleClick JS will search for it in Custom Element implementation (that's why I get an error in console). So how to pass a function? I tried "this.handle" but it also didn't work.
Due to the react Documentation you html should look like this
<sample-component onClick={handleClick}></sample-component>

React Ref Api's

So I am following video tutorials by Max on Udemy and in one of the lectures he is trying to explain Ref Api's in react 16.3
So here is what he did, Inside on of the container class (not App.js) he created a property known as this.lastref = React.createRef(); and then created a ref tag in return JSX code which looks like this ref={this.lastref} (This is the parent component)
Now in child component he created a method which looks like this
myFocus () {
this.lastref.current.focus()
}
and then in parent component, he again did something like this in componentDidMount lifecycle
componentDidMount() {
this.lastref.current.myFocus()
}
Now here are two questions which I have.
[Question Part]
First: How can he use this.lastref in child component? Is this because of the uni-directional (or one directional) flow from Parent to child (this.lastPersonRef is referred from ref={this.lastPersonRef} ?
Second: myFocus I believe happens to be static method so shouldn't he initiate it before using it?
[Code Example]
Here is what Parent Component should look like -> [person.js]
import React, { Component } from 'react';
import Person from './persons/person-s';
class Cpersons extends Component {
this.lastref = React.createRef()
componentDidMount() {
this.lastref.current.myFocus()
}
render (
return {
<Person
key={el.id}
click={this.props.cpdelete.bind(index)}
ref={this.lastref}
name={el.name}
age={el.age}
changed={(event) => this.props.cpchanged(event, el.id)} />
});
}
}
export default Cpersons
and this should be my child component -> [person-s.js]
import React, { Component } from 'react';
class Cppersons extends Component {
myFocus () {
this.lastref.current.focus()
}
render() {
//something
return (
<div> Something </div>
)
}
}
export default Cppersons;
ref has changed a lot in the React world and documentation regarding it is wildy different. I suggest you use the callback method.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.otherComponentRef = null; // Will be set after the first render
}
render() {
return [
<OtherComponent ref={el => this.otherComponentRef = el} />,
<ChildComponent reference={this.otherComponentRef} />
];
}
}
First: How can he use this.lastref in child component? Is this because
of the uni-directional (or one directional) flow from Parent to child
(this.lastPersonRef is referred from ref={this.lastPersonRef} ?
when a ref is created inside a class component like this,
this.myRef = React.CreateRef();
this.myRef is assigned a null value. Later when the component is mounted, React assigns this.myRef an object with the current property making this.myRef.current an object containing either:
the dom element that the ref is attached to, or
the component that the ref is attached
In your code, lastref is attached to the Person component like so,
<Person ref={this.lastref} .../>
which at
componentDidMount() {
this.lastref.current.myFocus()
}
React assigns the Person instance (component) to this.lastref.current, like this
// ~ with a bit of React magic under the hood
this.lastref.current = new Person();
Since myFocus is a method on the instance, it can be called by this.lastref.current.myFocus()
I encourage you to read more about React Ref and its expected behavior from React docs. If you find yourself stuck, you can read about how class inheritance work in Javascript which gives more insight to what is going on behind the scenes.
Second: myFocus I believe happens to be static method so shouldn't he
initiate it before using it?
it's really just the syntax being used from a different Javascript specification
class P {
constructor(props) {
super();
this.myRef = props.myRef
}
myFocus() {
console.log(this.myRef)
}
}
is equivalent to
class P {
myFocus() {
console.log(this.props.myRef)
}
}
in the eyes of the babel-loader from Babel which transpiles the Javascript in a typical React application created with create-react-app. myFocus will be a method of the Instance when it is instantiated in both cases.

React Executing a method inside a component

I have a component like this:
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props, context) {
super(props, context);
this.state = {
isActive: false,
}
}
showMyComponent() {
this.setState({
isActive: true,
});
}
hideMyComponent() {
this.setState({
isActive: false,
});
}
render() {
return (
<div>
<h1>Compoent Here</h1>
</div>
);
}
}
export default MyComponent;
Now, on my index.js I am adding several components.
...
<Header />
<Nave />
Can I now do something like this here:
MyComponent.showMyComponent();
Like you normally call a function?
If not, how is this done?
You can use references. In your render() method you can get the ref. e.g.
<MyComponent ref={ref => {this.myComponent = ref}}/>
You need to create a field myComponent and assign it to it. With that you can call it like this.myComponent.showMyComponent()
See here Refs and the DOM
Use State
You are thinking about react wrong. You should not have to call a components function like this ever.
You can pass a prop to the component that will make the component hide or show.
or wrap the component in a if in the parent. Use the parents state to hide or show the component.
Like
if (someCondition) {
<MyComponent />
}
It's doable, even if some people hates this option, cause it's not the official React way, true.
You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as this.refs.myTypeahead.reset()). In most cases, it's clearer to use the built-in React data flow instead of using refs imperatively.
But However, thinking out of the box, is not forbidden so you can use refs for this.
class Parent extends Component {
onSomeThing() {
// Call some method of myChild
this.myChild.myChildsPublicMethod()
}
render() {
return <MyChild ref={ref => { this.myChild = ref; }} />
}
}
// MyChild
// Just as demo using Pure components here.
// You could use the normal class notation..
const MyChild = () => <div>Ola</div>;
MyChild.someMethod = () => console.log('Ola');
More here https://zhenyong.github.io/react/docs/more-about-refs.html

Categories