skip to Main Content

Suppose the following:

import * as React from 'react';

const isMobile = true;

const App = () => {
    const swipeStart = React.useCallback(() => {}, []);
    const swipeMove = React.useCallback(() => {}, []);

    const Component = isMobile
        ? <div onTouchStart={swipeStart} onTouchMove={swipeMove}  />
        : <React.Fragment />;

    return (
        <Component>
            <span>child</span>
        </Component>
    );
}

How can I get this to work? I know that with styled-components or @emotion, there is an as prop, but I don’t want to install more libraries than necessary.

2

Answers


  1. It’s easies to assign Component to just the type of element.

    Then pass the props to the Component.

    Since React.Fragment won’t do anything with those props it gets the desired behaviour.

    (an if / else with return would also work)

    const { useState, useCallback } = React;
    
    const isMobile = true;
    
    const App = () => {
        const swipeStart = React.useCallback(() => {}, []);
        const swipeMove = React.useCallback(() => {}, []);
    
        const Component = isMobile ? 'div' : React.Fragment;
        
        return (
            <Component onTouchStart={swipeStart} onTouchMove={swipeMove}>
                <span>child</span>
            </Component>
        );
    }
    ReactDOM.render(<App />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>

    Should you don’t want to pass the props if it’s not needed.

    Another option is to make an empty object, and only add the props if isMobile. Then spread (...) the object to Component

    const { useState, useCallback } = React;
    
    const isMobile = true;
    
    const App = () => {
    
        const props = {};
        if (isMobile) {
            props.onTouchStart = React.useCallback(() => {}, []);;
            props.onTouchMove = React.useCallback(() => {}, []);;
        }
        
        const Component = isMobile ? 'div' : React.Fragment;
        
        return (
            <Component {...props}>
                <span>child</span>
            </Component>
        );
    }
    ReactDOM.render(<App />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>
    Login or Signup to reply.
  2. Make your Component into a component, currently its something called Component that contains some JSX.

    eg.
    const X = <div/> <- JSX

    const X = (p) => <div/> <- Component

    eg.

    const { useState, useCallback } = React;
    
    const isMobile = true;
    
    const App = () => {
        const swipeStart = React.useCallback(() => {}, []);
        const swipeMove = React.useCallback(() => {}, []);
    
        const Component = (p) => isMobile
            ? <div onTouchStart={swipeStart} onTouchMove={swipeMove} {...p}/>
            : <React.Fragment {...p}/>;
    
        return (
            <Component>
                <span>child</span>
            </Component>
        );
    }
    ReactDOM.render(<App />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>

    Note: {...p} is just a way of passing all the props, the main one in your case is children.

    Also seen as you mentioned Typescript, to keep Typescript happy you could just do ->

    const Component = (p: {children:React.ReactNode} ) => isMobile

    Also one issue with this, even if you have isMobile set to false, it’s going to create 2 useCallback’s that never get used. To get around this you could split this up.

    eg.

    
    const isMobile = true;
    
    const Component = isMobile
        ? (props:{children:React.ReactNode}) => {
            const swipeStart = React.useCallback(() => {}, []);
            const swipeMove = React.useCallback(() => {}, []);
            return <div onTouchStart={swipeStart} onTouchMove={swipeMove} {...props}/>
        }
        : (props:{children:React.ReactNode}) => <React.Fragment {...props}/>
        
    
    const App = () => {
        return (
            <Component>
                <span>child</span>
            </Component>
        );
    }
    

    In above if you set isMobile to false, useCallback is then never even used.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search