Table of contents
  1. Gists
    1. DynamicToolTip.js
    2. PrintPresetPage.js
    3. DynamicInternationalizedComponent.js
  2. Print separate page from current page
  3. Working with Canvas to create image
  4. Force an Update
  5. recursive component example
    1. template
    2. use case
  6. dynamic component
  7. Force Load a JS script file
  8. hyperlink
  9. Creating tags
  10. Access the Dom
  11. Merge Refs
    1. Use Case
  12. Conditional Rendering
  13. Axios
    1. Patch()
    2. Put()
    3. Interceptors
  14. used to navigate
  15. set local storage and access response header
  16. dangerouslySetInnerHTMLset html in a string
  17. useEffect()
    1. check for unmounting
    2. clean on unmounting
  18. Custom hook for form state management




Gists

DynamicToolTip.js

PrintPresetPage.js

DynamicInternationalizedComponent.js

Print separate page from current page

print pre-defined page

Working with Canvas to create image

IFrameComponent

Force an Update

const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);

recursive component example

template

const RecursiveWrapper = (props) => {
    const wrappedChildren = React.Children.map(props.children, (child) => {
        if (child.props && child.props.children) {
            return <RecursiveWrapper>{child.props.children}</RecursiveWrapper>;
        }

        return <div>{"children: 0"}</div>;
    });

    return (<React.Fragment>
        {`children: ${wrappedChildren.length}`}
        <div>{wrappedChildren}</div>
    </React.Fragment>);
};

use case

import React, {forwardRef, useCallback, useEffect, useState} from "react";
import PropTypes from "prop-types";
import {styled} from "@material-ui/styles";

const ChildContainer = styled("div")({});

const RecursiveComponent = forwardRef(({parentRefHandler, nodeNameOrIdArray, children, ...rest}, ref) => {
    const [containerRef, setContainerRef] = useState(null);
    const [elementMounted, setElementMounted] = useState();
    const [elementRef, setElementRef] = useState(null);
    const [childrenObject, setChildrenObject] = useState({});

    //passing callback to ref will set a container reference
    const setContainer = useCallback((element) => {
        if (element && !elementRef?.current && (nodeNameOrIdArray.indexOf(elementRef?.current?.nodeName) === -1 || nodeNameOrIdArray.indexOf(elementRef?.current?.nodeName) === -1)) {
            const innerContainer = element.firstElementChild;
            innerContainer.id = `RecursiveComponent_Child_Container`;
            setContainerRef(innerContainer);
        }
    }, [children],);

    //once container ref is set, we know the element has mounted
    useEffect(() => {
        if (containerRef) {
            setElementMounted(true);
        }
    }, [containerRef]);

    //once the element has mounted we need to query the child elements for any ids or element names that were passed in
    // from the nodeNameOrIdArray prop
    //if the element is found it will be added to the object with the name in the array
    // all unspecified children will be put in an array
    useEffect(() => {
        if (containerRef && elementMounted === true && containerRef.children) {
            const children = {unnamedChildren: []};

            for (const el of containerRef.children) {
                let foundElement;

                nodeNameOrIdArray.forEach((name, index) => {
                    foundElement = el.querySelector(name);
                    if (foundElement) {
                        foundElement.id = `${name}_${index}`
                                          ? `${name}_${index}`
                                          : `nested_element_id_${index}`;
                        children[name] = foundElement;
                    }
                });
                if (!foundElement) children.unnamedChildren.push(el);
            }

            setElementRef(ref);
            setChildrenObject(children);
        }
    }, [elementMounted]);

    //after seting a reference to the needed element we can run the method/handler that was passed down
    useEffect(() => {
        if (parentRefHandler && childrenObject) {
            parentRefHandler(childrenObject);
        }
    }, [childrenObject]);

    return (<ChildContainer ref={setContainer}>
        {React.cloneElement(children, rest)}
    </ChildContainer>);
},);

RecursiveComponent.propTypes = {
    children: PropTypes.instanceOf(Object), nodeNameOrIdArray: PropTypes.array,
};

RecursiveComponent.defaultProps = {
    nodeNameOrIdArray: [],
};
export default RecursiveComponent;

dynamic component

const WebApp = (props) => {
    return (<div>
        {config.map((componentName) => {
            componentMapping[componentName];
            return <Component/>;
        })}
    </div>);
};

Force Load a JS script file

import React, {useState, useEffect} from "react";
import "./style.css";

export default function App() {
    const [user, setUser] = useState(false);
    const [scriptLoadingState, setScriptLoadingState] = useState("IDLE");

    useEffect(() => {
        if (user) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.src = "https://www.google-analytics.com/analytics.js";
            script.onload = function () {
                setScriptLoadingState("LOADED");
            };
            script.onerror = function () {
                setScriptLoadingState("FAILED");
            };
            document.body.appendChild(script);
        }
    }, [user]);

    return (<div>
        <button
            onClick={() => setUser(true)}
            style=
        >
            Login
        </button>
        <h2>
            Script Loading State:{" "}
            <span
                style=
            >
          {scriptLoadingState}
        </span>
        </h2>
    </div>);
}

hyperlink

<Link to={`/products/${product.id}`}>{product.name}</Link>

Rather than <a>

Creating tags

Ul > li[(className = "test")];

Access the Dom

React Docs - Manipulate Dom With Ref

import {useRef} from "react";

function MyInput(props) {
    return <input {...props} />;
}

export default function MyForm() {
    const inputRef = useRef(null);

    function handleClick() {
        inputRef.current.focus();
    }

    return (<>
        <MyInput ref={inputRef}/>
        <button onClick={handleClick}>Focus the input</button>
    </>);
}

or

import {useRef} from "react";

const ref = useRef(null);
const element = <div ref={ref}/>;

// ...

ref.current; // DOM element
  • alt
export default function Component(props) {
    const nodeRef = useRef();

    useEffect(() => {
        console.log(nodeRef.current);
    }, []);

    // Root Node
    return <input ref={nodeRef}/>;
}

Merge Refs

When developing low level UI components, it is common to have to use a local ref but also support an external one using React.forwardRef.
Natively, React does not offer a way to set two refs inside
the ref property.
This is the goal of this small utility.

Today a ref can be a function or an object, tomorrow it could be another thing, who knows. This utility handles compatibility for you.

import type * as React from "react";

export function mergeRefs<T = any>(refs: Array<React.MutableRefObject<T> | React.LegacyRef<T> | undefined | null>): React.RefCallback<T> {
    return (value) => {
        refs.forEach((ref) => {
            if (typeof ref === "function") {
                ref(value);
            } else if (ref != null) {
               (ref as
                               React.MutableRefObject<T | null>
               ).current = value;
            }
        });
    };
}
  • Use Case

     import {mergeRefs} from "react-merge-refs";
       
     const Example = React.forwardRef(function Example(props, ref) {
        const localRef = React.useRef();
        return <div ref={mergeRefs([localRef, ref])}/>;
     });
    

Conditional Rendering

{
    error && <div className="alert alert-danger">{error}</div>;
}

Axios

Patch()

Used to update one or more properties

Axios.patch(apiEndpoint + "/" + post.id, {title: post.title});

Put()

Update all properties

axios.put(apiEndpoint + "/" + post.id, post);

Interceptors

axios.interceptors.response.use(success, error);
this.props.history.push("/");

used to navigate

this.props.history.push("/");
localStorage.setItem("token", response.headers["x-auth-token"]);

set local storage and access response header

need to have backend make headers visible> set local storage and access response header, need to have backend make headers visible

.header("access-control-expose-headers", "x-auth-token")

dangerouslySetInnerHTMLset html in a string

{ __html: '<p>' + result?.themeSummary + '. <i>Theme Of Significance.</i></p> '}

< Tooltip title={<div dangerouslySetInnerHTML={modifiedToolTip}/>} childrenDisplayStyle="inline">

useEffect()

used after browser repaints DOM

react will prioritize UI

React.useEffect(() => {
    // Will be invoked on the initial render
    // and all subsequent re-renders.
});
React.useEffect(() => {
    // Will be invoked on the initial render
    // and when "id" or "authed" changes.
}, [id, authed]);
React.useEffect(() => {
    // Will only be invoked on the initial render
}, []);
React.useEffect(() => {
    return () => {
        // invoked right before invoking
        // the new effect on a re-render AND
        // right before removing the component
        // from the DOM
    };
});

check for unmounting

useEffect(() => {
    return () => console.log("unmounting...");
});

clean on unmounting

useEffect(() => {
    let isMounted = true;
    register("interviewModelId");

    fetchInterviewModels().then((data) => {
        if (isMounted) setAssessmentChoiceList(data);
        setSelectedInterviewModel(data);
    });

    if (!groupDetails) {
        register({name: "assessmentOrderIds"}, {
            required: errorMessages.assIdsRequired, validate: (value) => value.length <= maxGroupMembers || errorMessages.maxGroupMembers,
        },);
    }
    return () => {
        isMounted = false;
    };
}, [errorMessages.assIdsRequired, errorMessages.maxGroupMembers, groupDetails, maxGroupMembers, register, setSelectedInterviewModel,]);

Custom hook for form state management

Goal: defining a custom useForm hook for managing the state of our forms.
This can have some initial state, mainly used for precompleting update forms, but can also provide empty form
fields.> Problem: when we want to operate on a precompleted form, altough we pass all the data for our fields, the form is not being precompleted
and its fields are empty.

export default function useForm(initial = {}) {
    const [inputs, setInputs] = useState(initial);
    const handleChange = (e) => {
        let {value, name, type} = e.target;

        if (type === "number") {
            value = parseInt(value);
        }
        if (type === "file") {
            [value] = e.target.files;
        }

        setInputs({
            ...inputs, [name]: value,
        });
    };

    const resetForm = () => {
        setInputs(initial);
    };

    const clearForm = () => {
        const blankState = Object.fromEntries(Object.entries(inputs).map(([key, value]) => [key, ""]),);

        setInputs(blankState);
    };

    return {
        inputs, clearForm, resetForm, handleChange,
    };
}

This is what happens:

  1. We pass an initial state object which is undefined (server-side rendered) until the GQL query loads.
  2. This initial object (which is undefined) populates the form fields making them empty.
  3. After the query loads, the initial state object is repassed to the useForm hook, but the DOM is not rerendered => a possible solution is to make
    use of the useEffect() hook for forcing rerendering.
  4. We cannot watch for changes directly on the initial object and reassign it using setInputs, because it triggers the useEffect callback once again
    and again and again when altering its value,
    causing an infinite loop.
  5. The solution is to watch for changes on a string joined by the values of the initial object.
    When that changes from undefined to the GraphQL query results, the useEffect callback is called and it
    initializes and rerenders the fields correspondingly.

An example implementation could be:

const initialValues = Object.values(initial).join("");
useEffect(() => {
    setInputs(initial);
}, [initialValues]);

Now the form precompletion works fine using our custom useForm() hook.