react icon indicating copy to clipboard operation
react copied to clipboard

Any example of custom component with react functional component.

Open aryanisml opened this issue 2 years ago • 2 comments

aryanisml avatar Nov 10 '22 12:11 aryanisml

import React from 'react';

function CustomButton(props) { return ( <button onClick={props.onClick}> {props.text} ); }

export default CustomButton;

import React from 'react'; import CustomButton from './CustomButton';

function App() { function handleClick() { console.log('Button was clicked!'); }

return (

My App

<CustomButton text="Click Me" onClick={handleClick} />
); }

export default App;

dheerajlakhotia avatar Mar 31 '23 16:03 dheerajlakhotia

Heres what we use to support functional components. Hope this helps others who come across this issue.

/**
 * The following class is derived from the ReactComponent class used in @formio/react.
 * However it adds support for functional react components, as formios implementation only supports
 * class based components.
 * https://github.com/formio/react/blob/master/src/components/ReactComponent.jsx
 * 
 */

import { Formio } from 'formiojs';
const Field = (Formio as any).Components.components.field;

export default class ReactComponent extends Field {
    /**
     * This is the first phase of component building where the component is instantiated.
     *
     * @param component - The component definition created from the settings form.
     * @param options - Any options passed into the renderer.
     * @param data - The submission data where this component's data exists.
     */
    constructor(component: any, options: any, data: any) {
        super(component, options, data);
        this.reactInstance = null;
    }

    /**
     * This method is called any time the component needs to be rebuilt. It is most frequently used to listen to other
     * components using the this.on() function.
     */
    init() {
        return super.init();
    }

    /**
     * This method is called before the component is going to be destroyed, which is when the component instance is
     * destroyed. This is different from detach which is when the component instance still exists but the dom instance is
     * removed.
     */
    destroy() {
        return super.destroy();
    }
    /**
     * This method is called before a form is submitted.
     * It is used to perform any necessary actions or checks before the form data is sent.
     *
     */

    beforeSubmit() {
        return super.beforeSubmit();
    }

    /**
     * The second phase of component building where the component is rendered as an HTML string.
     *
     * @returns {string} - The return is the full string of the component
     */
    render() {
        // For react components, we simply render as a div which will become the react instance.
        // By calling super.render(string) it will wrap the component with the needed wrappers to make it a full component.
        return super.render(`<div ref="react-${this.id}"></div>`);
    }

    /**
     * Callback ref to store a reference to the node.
     *
     * @param element - the node
     */
    setReactInstance(element: any) {
        this.reactInstance = element;
    }

    /**
     * The third phase of component building where the component has been attached to the DOM as 'element' and is ready
     * to have its javascript events attached.
     *
     * @param element
     * @returns {Promise<void>} - Return a promise that resolves when the attach is complete.
     */
    attach(element: any) {
        super.attach(element);

        // The loadRefs function will find all dom elements that have the "ref" setting that match the object property.
        // It can load a single element or multiple elements with the same ref.
        this.loadRefs(element, {
            [`react-${this.id}`]: 'single',
        });

        const reactInstanceRef = this.refs[`react-${this.id}`];

        if (reactInstanceRef) {
            this.attachReact(reactInstanceRef, () => this.setReactInstance(reactInstanceRef));
            if (this.shouldSetValue) {
                this.setValue(this.dataForSetting);
                this.updateValue(this.dataForSetting);
            }
        }
        return Promise.resolve();
    }

    /**
     * The fourth phase of component building where the component is being removed from the page. This could be a redraw
     * or it is being removed from the form.
     */
    detach() {
        if (this.refs[`react-${this.id}`]) {
            this.detachReact(this.refs[`react-${this.id}`]);
        }
        super.detach();
    }

    /**
     * Override this function to insert your custom component.
     *
     * @param element
     * @param ref - callback ref
     */
    attachReact(element: any, ref?: any) {
        return;
    }

    /**
     * Override this function.
     */
    detachReact(element: any) {
        return;
    }

    /**
     * Something external has set a value and our component needs to be updated to reflect that. For example, loading a submission.
     *
     * @param value
     */
    setValue(value: any) {
        this.dataValue = value;
        if (this.reactInstance)
            this.attachReact(this.reactInstance);
    }

    /**
     * The user has changed the value in the component and the value needs to be updated on the main submission object and other components notified of a change event.
     *
     * @param value
     */
    updateValue = (value: any, flags?: any) => {
        flags = flags || {};
        const newValue = value === undefined || value === null ? this.getValue() : value;
        const changed = (newValue !== undefined) ? this.hasChanged(newValue, this.dataValue) : false;
        this.dataValue = Array.isArray(newValue) ? [...newValue] : newValue;

        this.updateOnChange(flags, changed);
        return changed;
    };

    /**
     * Get the current value of the component. Should return the value set in the react component.
     *
     * @returns {*}
     */
    getValue() {
        if (this.dataValue) {
            return this.dataValue;
        }
        return this.defaultValue;
    }

    /**
     * Override normal validation check to insert custom validation in react component.
     *
     * @param data
     * @param dirty
     * @param rowData
     * @returns {boolean}
     */
    checkValidity(data: any, dirty: boolean, rowData: any) {
        const valid = super.checkValidity(data, dirty, rowData);
        if (!valid) {
            return false;
        }
        return this.validate(data, dirty, rowData);
    }

    /**
     * Do custom validation.
     *
     * @param data
     * @param dirty
     * @param rowData
     * @returns {boolean}
     */
    validate(data: any, dirty: boolean, rowData: any) {
        return true;
    }
}

AnthonyPhan avatar Jun 08 '23 06:06 AnthonyPhan

An example updated React 18 + Custom Component working! Demo

sabato-galasso avatar Apr 30 '24 08:04 sabato-galasso

Thanks for the contributions @sabato-galasso and @AnthonyPhan Closing this issue as resolved.

lane-formio avatar May 06 '24 16:05 lane-formio